GroupBy is a LINQ functionality which allows to group items from a collection based on a given key. It’s an equivalent to SQL’s GROUP BY clause. In this article we’ll show various examples of usage, starting from the simplest grouping by one key, through grouping by multiple keys, custom result selector and all of that in two variants: Lambda and Query expression.
Let’s prepare sample data which we could operate on to present various examples of group by. For this purpose we define Person class and create 10 objects collected within a list.
1 2 3 4 5 6 |
public class Person { public string Forename { get; set; } public string Surname { get; set; } public int Age { get; set; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var people = new List<Person>() { new Person() { Forename = "John", Surname = "Smith", Age = 33 }, new Person() { Forename = "Michael", Surname = "Jones", Age = 41 }, new Person() { Forename = "Susan", Surname = "Taylor", Age = 21 }, new Person() { Forename = "Michael", Surname = "Evans", Age = 41 }, new Person() { Forename = "James", Surname = "Wilson", Age = 39 }, new Person() { Forename = "Michael", Surname = "Johnson", Age = 35 }, new Person() { Forename = "Susan", Surname = "Davies", Age = 21 }, new Person() { Forename = "Susan", Surname = "Robinson", Age = 47 }, new Person() { Forename = "John", Surname = "Wright", Age = 44 }, new Person() { Forename = "Susan", Surname = "Walker", Age = 21 } }; |
Single key selector
First scenario is to group people collection by forename.
Lambda expression
1 |
var groups = people.GroupBy(x => x.Forename); |
Query expression
1 2 |
var groups = from p in people group p by p.Forename; |
Both Lambda and Query expression generates the same results which can be displayed with foreach loops. We need to use two of them as first level is collection of groups and each group contains collection of people within particular group.
1 2 3 4 5 6 7 8 9 |
foreach (var group in groups) { Console.WriteLine($"Group key: {group.Key}"); foreach (var person in group) { Console.WriteLine($"Forename: {person.Forename}, Surname: {person.Surname}, Age: {person.Age}"); } Console.WriteLine(); } |
In the results we can see four groups, where people within each group share the same forename.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Group key: John Forename: John, Surname: Smith, Age: 33 Forename: John, Surname: Wright, Age: 44 Group key: Michael Forename: Michael, Surname: Jones, Age: 41 Forename: Michael, Surname: Evans, Age: 41 Forename: Michael, Surname: Johnson, Age: 35 Group key: Susan Forename: Susan, Surname: Taylor, Age: 21 Forename: Susan, Surname: Davies, Age: 21 Forename: Susan, Surname: Robinson, Age: 47 Forename: Susan, Surname: Walker, Age: 21 Group key: James Forename: James, Surname: Wilson, Age: 39 |
Multiple key selector
Above example was the simplest but it is also possible to group by multiple keys. To present it let’s group our people collection by both forename and age.
Lamda expression
1 |
var groups = people.GroupBy(x => (x.Forename, x.Age)); |
Query expression
1 2 |
var groups = from p in people group p by (p.Forename, p.Age); |
Results can be printed using the code from previous example. This time we’ve got seven groups of people, where each group contains only people with the same forename and age.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Group key: (John, 33) Forename: John, Surname: Smith, Age: 33 Group key: (Michael, 41) Forename: Michael, Surname: Jones, Age: 41 Forename: Michael, Surname: Evans, Age: 41 Group key: (Susan, 21) Forename: Susan, Surname: Taylor, Age: 21 Forename: Susan, Surname: Davies, Age: 21 Forename: Susan, Surname: Walker, Age: 21 Group key: (James, 39) Forename: James, Surname: Wilson, Age: 39 Group key: (Michael, 35) Forename: Michael, Surname: Johnson, Age: 35 Group key: (Susan, 47) Forename: Susan, Surname: Robinson, Age: 47 Group key: (John, 44) Forename: John, Surname: Wright, Age: 44 |
Custom result selector
Last example I’d like to present here is combining group by with custom result selector. In this scenario each group in the returned collection has GroupName property containing a key name, GroupSize property containing number of items in a group, and GroupItems containing list of people in a group.
Lambda expression
1 2 3 4 5 6 |
var groups = people.GroupBy(x => (x.Forename)).Select(x => new { GroupName = x.Key, GroupSize = x.Count(), GroupItems = x.ToList() }); |
Query expression
1 2 3 4 5 6 7 8 9 |
var groups = from p in people group p by p.Forename into g select new { GroupName = g.Key, GroupSize = g.Count(), GroupItems = g.ToList() }; |
Code to display results needs to be slightly modified to reflect above selector changes.
1 2 3 4 5 6 7 8 9 10 |
foreach (var group in groups) { Console.WriteLine($"Group name: {group.GroupName}"); Console.WriteLine($"Group size: {group.GroupSize}"); foreach (var person in group.GroupItems) { Console.WriteLine($"Forename: {person.Forename}, Surname: {person.Surname}, Age: {person.Age}"); } Console.WriteLine(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Group name: John Group size: 2 Forename: John, Surname: Smith, Age: 33 Forename: John, Surname: Wright, Age: 44 Group name: Michael Group size: 3 Forename: Michael, Surname: Jones, Age: 41 Forename: Michael, Surname: Evans, Age: 41 Forename: Michael, Surname: Johnson, Age: 35 Group name: Susan Group size: 4 Forename: Susan, Surname: Taylor, Age: 21 Forename: Susan, Surname: Davies, Age: 21 Forename: Susan, Surname: Robinson, Age: 47 Forename: Susan, Surname: Walker, Age: 21 Group name: James Group size: 1 Forename: James, Surname: Wilson, Age: 39 |