Группировка по нескольким столбцам с помощью LINQ в C#
Решение: группировка с помощью анонимного типа
Для группировки по нескольким полям в LINQ используется анонимный тип в качестве ключа. Это позволяет объединить несколько полей в один составной ключ. После группировки вы можете обращаться к отдельным полям ключа через свойства анонимного типа.
var result = orders
.GroupBy(o => new { o.CustomerId, o.ProductId })
.Select(g => new
{
CustomerId = g.Key.CustomerId,
ProductId = g.Key.ProductId,
TotalQuantity = g.Sum(o => o.Quantity),
TotalPrice = g.Sum(o => o.Price)
})
.ToList();
Объяснение:
GroupBy(o => new { o.CustomerId, o.ProductId })— создаёт группы с составным ключом. Каждая группа содержит все заказы с одинаковым сочетанием CustomerId и ProductId.g.Key.CustomerIdиg.Key.ProductId— обращение к отдельным полям ключа.g.Sum(o => o.Quantity)— суммирует Quantity для всех элементов в группе.g.Sum(o => o.Price)— суммирует Price для всех элементов в группе.
Полный пример с тестовыми данными
using System;
using System.Collections.Generic;
using System.Linq;
public class Order
{
public int CustomerId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}
public class Program
{
public static void Main()
{
var orders = new List<Order>
{
new Order { CustomerId = 1, ProductId = 101, Quantity = 2, Price = 10.5m },
new Order { CustomerId = 1, ProductId = 101, Quantity = 3, Price = 10.5m },
new Order { CustomerId = 1, ProductId = 102, Quantity = 1, Price = 25.0m },
new Order { CustomerId = 2, ProductId = 101, Quantity = 5, Price = 10.5m },
new Order { CustomerId = 2, ProductId = 103, Quantity = 1, Price = 50.0m }
};
var result = orders
.GroupBy(o => new { o.CustomerId, o.ProductId })
.Select(g => new
{
CustomerId = g.Key.CustomerId,
ProductId = g.Key.ProductId,
TotalQuantity = g.Sum(o => o.Quantity),
TotalPrice = g.Sum(o => o.Price)
})
.ToList();
foreach (var item in result)
{
Console.WriteLine($"Customer {item.CustomerId}, Product {item.ProductId}: " +
$"Qty={item.TotalQuantity}, Total={item.TotalPrice:C}");
}
}
}
Результат:
Customer 1, Product 101: Qty=5, Total=$52.50
Customer 1, Product 102: Qty=1, Total=$25.00
Customer 2, Product 101: Qty=5, Total=$52.50
Customer 2, Product 103: Qty=1, Total=$50.00
Важные замечания
- Анонимный тип автоматически реализует Equals и GetHashCode, поэтому сравнение ключей работает корректно.
- Порядок полей в анонимном типе важен:
new { o.CustomerId, o.ProductId }иnew { o.ProductId, o.CustomerId }— это разные ключи. - Если нужно сохранить результат для дальнейшего использования, можно создать именованный класс вместо анонимного типа.
Альтернативный подход: использование кортежей (C# 7.0+)
Начиная с C# 7.0, можно использовать кортежи для группировки. Это даёт более читаемый синтаксис и упрощает обращение к ключам без анонимных типов:
var result = orders
.GroupBy(o => (o.CustomerId, o.ProductId))
.Select(g => new
{
CustomerId = g.Key.CustomerId,
ProductId = g.Key.ProductId,
TotalQuantity = g.Sum(o => o.Quantity),
TotalPrice = g.Sum(o => o.Price)
})
.ToList();
Преимущества кортежей:
- Более лаконичный синтаксис:
(o.CustomerId, o.ProductId)вместоnew { o.CustomerId, o.ProductId }. - Элементы кортежа имеют имена по умолчанию (Item1, Item2), но вы можете задать свои имена:
(int CustId, int ProdId). - Кортежи — значимые типы, что может дать небольшой прирост производительности.
Недостаток: Если вам нужно передать результат в другой метод, анонимные типы проще преобразовать в именованные классы, чем кортежи (хотя кортежи тоже можно использовать как возвращаемые типы).
Оба подхода (анонимные типы и кортежи) корректно решают задачу. Выбор зависит от версии C# и личных предпочтений.
Ответов пока нет.
Войдите чтобы оставить ответ