How to use LINQ GroupBy in C#
The LINQ GroupBy() method organizes elements into groups based on a key selector. It returns an IEnumerable<IGrouping<TKey, TElement>> where each IGrouping holds the key and the matching elements.
Each group is itself an IEnumerable<T>, so you can apply further LINQ operations (Count, Sum, Average, etc.) to each group.
GroupBy() uses deferred execution. The grouping materializes when you iterate, call .ToList(), or perform an aggregation.
Basic GroupBy
C# Example Code
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Group into even and odd
var groups = numbers.GroupBy(n => n % 2 == 0 ? "Even" : "Odd");
foreach (var group in groups)
{
Console.WriteLine($"{group.Key}: {string.Join(", ", group)}");
}
// Output:
// Odd: 1, 3, 5, 7, 9
// Even: 2, 4, 6, 8, 10Group Objects by Property
C# Example Code
var people = new List<Person>
{
new Person("Alice", "Engineering"),
new Person("Bob", "Marketing"),
new Person("Carol", "Engineering"),
new Person("Dave", "Marketing"),
new Person("Eve", "Engineering")
};
var byDepartment = people.GroupBy(p => p.Department);
foreach (var dept in byDepartment)
{
Console.WriteLine($"\n{dept.Key}:");
foreach (var person in dept)
Console.WriteLine($" - {person.Name}");
}
// Output:
// Engineering:
// - Alice
// - Carol
// - Eve
// Marketing:
// - Bob
// - Dave
record Person(string Name, string Department);Count per Group
C# Example Code
var orders = new List<Order>
{
new Order("Alice", "Electronics"),
new Order("Bob", "Books"),
new Order("Alice", "Books"),
new Order("Bob", "Electronics"),
new Order("Carol", "Electronics")
};
// Count orders per customer
var orderCounts = orders
.GroupBy(o => o.Customer)
.Select(g => new { Customer = g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count);
foreach (var row in orderCounts)
Console.WriteLine($"{row.Customer}: {row.Count} orders");
// Output:
// Alice: 2 orders
// Bob: 2 orders
// Carol: 1 orders
record Order(string Customer, string Category);Aggregate Within Groups (Sum, Average, Max)
C# Example Code
var sales = new List<Sale>
{
new Sale("North", 1500m),
new Sale("South", 2200m),
new Sale("North", 800m),
new Sale("East", 3100m),
new Sale("South", 950m),
new Sale("North", 600m)
};
var regionStats = sales
.GroupBy(s => s.Region)
.Select(g => new
{
Region = g.Key,
Total = g.Sum(s => s.Amount),
Average = g.Average(s => s.Amount),
Max = g.Max(s => s.Amount),
Count = g.Count()
})
.OrderByDescending(r => r.Total);
foreach (var r in regionStats)
Console.WriteLine($"{r.Region}: Total={r.Total:C0}, Avg={r.Average:C0}, Max={r.Max:C0}, Count={r.Count}");
// Output:
// North: Total=$2,900, Avg=$967, Max=$1,500, Count=3
// East: Total=$3,100, Avg=$3,100, Max=$3,100, Count=1
// South: Total=$3,150, Avg=$1,575, Max=$2,200, Count=2
record Sale(string Region, decimal Amount);Group by Multiple Keys
C# Example Code
var employees = new List<Employee>
{
new Employee("Alice", "Engineering", "Senior"),
new Employee("Bob", "Marketing", "Junior"),
new Employee("Carol", "Engineering", "Junior"),
new Employee("Dave", "Marketing", "Senior"),
new Employee("Eve", "Engineering", "Senior")
};
// Group by department + level using an anonymous type key
var grouped = employees
.GroupBy(e => new { e.Department, e.Level })
.Select(g => new
{
g.Key.Department,
g.Key.Level,
Names = string.Join(", ", g.Select(e => e.Name)),
Count = g.Count()
});
foreach (var g in grouped)
Console.WriteLine($"{g.Department} / {g.Level}: {g.Names} ({g.Count})");
// Output:
// Engineering / Senior: Alice, Eve (2)
// Marketing / Junior: Bob (1)
// Engineering / Junior: Carol (1)
// Marketing / Senior: Dave (1)
record Employee(string Name, string Department, string Level);Method Syntax vs Query Syntax
C# Example Code
var people = new List<Person>
{
new Person("Alice", "Engineering"),
new Person("Bob", "Marketing"),
new Person("Carol", "Engineering")
};
// Method syntax
var methodGroups = people
.GroupBy(p => p.Department)
.Select(g => new { Dept = g.Key, Count = g.Count() });
// Query syntax
var queryGroups = from p in people
group p by p.Department into g
select new { Dept = g.Key, Count = g.Count() };GroupBy vs ToLookup
Both group elements by a key, but they differ in when they execute and how you access the result.
C# Example Code
var people = new List<Person>
{
new Person("Alice", "Engineering"),
new Person("Bob", "Marketing"),
new Person("Carol", "Engineering")
};
// GroupBy — deferred execution, enumerates source once per group
var groupBy = people.GroupBy(p => p.Department);
// ToLookup — immediate execution, O(1) key lookup
var lookup = people.ToLookup(p => p.Department);
// Access by key (safe — returns empty sequence if key missing)
foreach (var person in lookup["Engineering"])
Console.WriteLine(person.Name);
// Output:
// Alice
// CarolGroupBy() | ToLookup() | |
|---|---|---|
| Execution | Deferred | Immediate |
| Access by key | Iterate groups | O(1) index lookup |
| Missing key | Skipped | Empty sequence returned |
| Use case | LINQ pipeline | Repeated lookups on same data |