← Все вопросы

Как сгруппировать список объектов и посчитать сумму по группам через LINQ?

Задан 23 месяца назад1.4к просмотров2 ответа
7

Есть список заказов, у каждого есть категория и сумма. Мне надо посчитать суммарную выручку по каждой категории и отсортировать по убыванию. На SQL я бы написал GROUP BY ... ORDER BY SUM(...) DESC, а вот в LINQ через GroupBy ничего не получается — не пойму, что возвращает группа.

record Order(string Category, decimal Amount);

var orders = new List<Order>
{
    new("Книги", 300),
    new("Еда", 150),
    new("Книги", 200),
    new("Еда", 400),
    new("Игры", 1000),
};

Как из этого получить пары «категория → сумма», отсортированные по сумме? GroupBy возвращает что-то странное, с чем я не знаю что делать.

2 ответа

13
✓ Принятый ответ — помог автору

GroupBy возвращает последовательность групп, где каждая группа — это IGrouping<TKey, TElement>. У группы есть свойство Key (то, по чему группировали) и сама группа — это коллекция элементов, по которой можно делать Sum, Count и т.п.

Дальше через Select превращаешь каждую группу в нужный объект, а потом OrderByDescending:

var result = orders
    .GroupBy(o => o.Category)
    .Select(g => new 
    {
        Category = g.Key,
        Total = g.Sum(o => o.Amount)
    })
    .OrderByDescending(x => x.Total)
    .ToList();

foreach (var row in result)
    Console.WriteLine($"{row.Category}: {row.Total}");

// Игры: 1000
// Книги: 500
// Еда: 550  -> на самом деле Еда выше Книг

Ключевая мысль: g.Key — это значение группировки, а сам g — коллекция элементов группы, к которой применяешь агрегат (Sum, Average, Max). Это прямой аналог GROUP BY + SUM из SQL.

6

Если не хочешь анонимный тип, можно сразу собрать словарь — иногда это удобнее для дальнейшей работы:

Dictionary<string, decimal> totals = orders
    .GroupBy(o => o.Category)
    .ToDictionary(g => g.Key, g => g.Sum(o => o.Amount));

// totals["Книги"] == 500

А для сортировки по сумме потом просто totals.OrderByDescending(kv => kv.Value). Но для вывода вариант с анонимным типом из принятого ответа чище.

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект