C# – Examples of using GroupBy() (Linq)

Here’s an example of using the Linq GroupBy() method to group coders by language:

using System.Linq;

var coders = new List<Coder>()
{
    new Coder() { Id = 1, Language = "C#", YearsExperience = 1 },
    new Coder() { Id = 2, Language = "Java", YearsExperience = 10  },
    new Coder() { Id = 3, Language = "Python", YearsExperience = 5},
    new Coder() { Id = 4, Language = "JavaScript", YearsExperience = 2 },
    new Coder() { Id = 5, Language = "C#", YearsExperience = 15 },
    new Coder() { Id = 6, Language = "Java", YearsExperience = 10  },
};

var codersByLanguage = coders.GroupBy(c => c.Language);

foreach (var languageGroup in codersByLanguage)
{
    var codersInLanguageGroup = System.Text.Json.JsonSerializer.Serialize(languageGroup);
    Console.WriteLine($"{languageGroup.Key} coders = {codersInLanguageGroup}");
    Console.WriteLine();
}
Code language: C# (cs)

This example outputs the following:

C# coders = [{"Id":1,"Language":"C#","YearsExperience":1},{"Id":5,"Language":"C#","YearsExperience":15}]

Java coders = [{"Id":2,"Language":"Java","YearsExperience":10},{"Id":6,"Language":"Java","YearsExperience":10}]

Python coders = [{"Id":3,"Language":"Python","YearsExperience":5}]

JavaScript coders = [{"Id":4,"Language":"JavaScript","YearsExperience":2}]Code language: plaintext (plaintext)

GroupBy() produces groups that contain the grouping key (i.e. Language) and the list of objects in the group (i.e. the Coder objects).

The GroupBy() syntax is complex because it supports many scenarios. You can select one or more grouping keys, change the grouped elements, and alter the grouped results (such as aggregating values per group). In this article, I’ll show examples of using GroupBy() in these various ways.

Note: I’ll use the list of coders shown above in all of the examples.

Looping through the grouped objects

GroupBy() returns groups. Each group contains a grouping key and a collection of objects in the group. You can loop over the groups and the objects within each group. Here’s an example:

var codersByLanguage = coders.GroupBy(c => c.Language);

foreach (var languageGroup in codersByLanguage)
{
    Console.WriteLine($"Coders using {languageGroup.Key}:");

    foreach(var coder in languageGroup)
    {
        Console.WriteLine($"\tCoder {coder.Id} has {coder.YearsExperience} yr(s) exp");
    }
}
Code language: C# (cs)

When you loop over the groups, you get access to the grouping key (i.e. Language), and the grouped objects (i.e. Coder objects). This outputs the following:

Coders using C#:
        Coder 1 has 1 yr(s) exp
        Coder 5 has 15 yr(s) exp
Coders using Java:
        Coder 2 has 10 yr(s) exp
        Coder 6 has 10 yr(s) exp
Coders using Python:
        Coder 3 has 5 yr(s) exp
Coders using JavaScript:
        Coder 4 has 2 yr(s) expCode language: plaintext (plaintext)

Group by key selector

You always have to specify a grouping key. To do this, you pass in a lambda like this:

coders.GroupBy(c => c.Language);
Code language: C# (cs)

This means List<Coder> will be grouped by the Coder.Language property. You can group by one or more properties.

Group by multiple properties

To group by multiple properties, select the subset of properties you want to use for the key into an anonymous type in the key selector lambda:

var codersByLanguage = coders.GroupBy(c => new { c.Language, c.YearsExperience });

foreach (var languageGroup in codersByLanguage)
{
    Console.WriteLine($"{languageGroup.Key.Language} coders with {languageGroup.Key.YearsExperience} yrs exp:");

    foreach (var coder in languageGroup)
    {
        Console.WriteLine($"\tCoder {coder.Id}");
    }
}
Code language: C# (cs)

This outputs the following:

Coders that use C# with 1 yrs exp:
        Coder 1
Coders that use Java with 10 yrs exp:
        Coder 2
        Coder 6
Coders that use Python with 5 yrs exp:
        Coder 3
Coders that use JavaScript with 2 yrs exp:
        Coder 4
Coders that use C# with 15 yrs exp:
        Coder 5Code language: plaintext (plaintext)

Grouped element selector

The key selector tells GroupBy() what to use as the grouping key. The element selector tells it what to use as the grouped objects. By default, it will select the objects from the list you’re grouping:

coders.GroupBy(c => c.Language, elementSelector: c => c);
Code language: C# (cs)

This is grouping List<Coder> by Coder.Language and selecting Coder objects as the grouped elements.

You can override this default behavior by passing in your own element selector lambda. For example, instead of selecting the whole Coder object as the grouped element, you can select one or more properties as I’ll show below.

Select a single property

Let’s say you want to group coders by language and only want the id in the groups. To do that, pass in a lambda for the elementSelector parameter:

var coderIdsByLanguage = coders.GroupBy(c => c.Language, elementSelector: c => c.Id);

foreach (var languageGroup in coderIdsByLanguage)
{
    var idCSV = string.Join(",", languageGroup.Select(i => i));
    Console.WriteLine($"{languageGroup.Key} coder ids: {idCSV}");
}
Code language: C# (cs)

Note: I’m specifying the named parameter – elementSelector – here for emphasis. It’s not necessary.

This outputs the following:

C# coder ids: 1,5
Java coder ids: 2,6
Python coder ids: 3
JavaScript coder ids: 4Code language: plaintext (plaintext)

Select multiple properties

To use multiple properties as the grouped elements, select the properties into an anonymous type in the elementSelector lambda. For example, let’s say you want to group coders by language and want the id and years of experience in the groups.

var codersByLanguage = coders.GroupBy(c => c.Language, elementSelector: c => new { c.Id, c.YearsExperience });

foreach (var languageGroup in codersByLanguage)
{
    Console.WriteLine($"Coders using {languageGroup.Key}: {JsonSerializer.Serialize(languageGroup)}");
}
Code language: C# (cs)

Note: This is just like how when you want to use multiple properties for the key, you have to use an anonymous type in the keySelector lambda.

This outputs the following:

Coders using C#: [{"Id":1,"YearsExperience":1},{"Id":5,"YearsExperience":15}]

Coders using Java: [{"Id":2,"YearsExperience":10},{"Id":6,"YearsExperience":10}]

Coders using Python: [{"Id":3,"YearsExperience":5}]

Coders using JavaScript: [{"Id":4,"YearsExperience":2}]Code language: plaintext (plaintext)

Grouped result selector

The result selector function is called on grouped results, passing in the group key and grouped objects. You can use this to alter the grouped results.

coders.GroupBy(c => c.Language, resultSelector: (key, list) => new { Language=key, Coders=list });
Code language: C# (cs)

This is an alternative to using a separate Select() on the GroupBy() output.

One way to use this is to get aggregate values per group (i.e. Count, Sum, Min, Max).

Select an aggregate value per group

Let’s say you want the count of coders per language group. To do that, in the resultSelector lambda, call Count() on the passed in list of grouped objects:

var codersByLanguage = coders.GroupBy(c => c.Language, 
    resultSelector: (key, list) => new { Language = key, NumCoders = list.Count() });

foreach (var languageGroup in codersByLanguage)
{
    Console.WriteLine($"# coders using {languageGroup.Language}: {languageGroup.NumCoders}");
}
Code language: C# (cs)

This outputs the following:

# coders using C#: 2
# coders using Java: 2
# coders using Python: 1
# coders using JavaScript: 1Code language: plaintext (plaintext)