C#idiomatic C#
grouping-datacsharp/linq/standard-query-operators

LINQ

Grouping Data

Grouping puts data into groups of elements that share an attribute. Learn about the standard query operator methods in LINQ in C# that group data elements.

  • page csharp/linq/standard-query-operators/grouping-data
  • updated 05/29/2024

Grouping refers to the operation of putting data into groups so that the elements in each group share a common attribute. The following illustration shows the results of grouping a sequence of characters. The key for each group is the character.

Diagram that shows a LINQ Grouping operation


author: BillWagner ms.author: wiwagn ms.topic: include ms.date: 05/29/2024

The standard query operator methods that group data elements are listed in the following table.

Method Name Description C# Query Expression Syntax More Information
GroupBy Groups elements that share a common attribute. An System.Linq.IGrouping object represents each group. group … by …

-or-

group … by … into …
System.Linq.Enumerable.GroupBy

System.Linq.Queryable.GroupBy
ToLookup Inserts elements into a System.Linq.Lookup (a one-to-many dictionary) based on a key selector function. Not applicable. System.Linq.Enumerable.ToLookup

The following code example uses the group by clause to group integers in a list according to whether they're even or odd.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupOverview.csOverviewSampleQuerySyntax

csharp
namespace StandardQueryOperators;
internal class GroupOverview
{
    public static void RunAllSnippets()
    {
        Console.WriteLine("Group Overview");
        OverviewSampleQuery();
        Console.WriteLine("Group Overview Method");
        OverviewSampleMethod();
    }

    private static void OverviewSampleQuery()
    {
        // <OverviewSampleQuerySyntax>
        List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];

        IEnumerable<IGrouping<int, int>> query = from number in numbers
                                                 group number by number % 2;

        foreach (var group in query)
        {
            Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:");
            foreach (int i in group)
            {
                Console.WriteLine(i);
            }
        }
        // </OverviewSampleQuerySyntax>
    }

    private static void OverviewSampleMethod()
    {
        // <OverviewSampleMethodSyntax>
        List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];

        IEnumerable<IGrouping<int, int>> query = numbers
            .GroupBy(number => number % 2);

        foreach (var group in query)
        {
            Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:");
            foreach (int i in group)
            {
                Console.WriteLine(i);
            }
        }
        // </OverviewSampleMethodSyntax>
    }
}

The equivalent query using method syntax is shown in the following code:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupOverview.csOverviewSampleMethodSyntax

csharp
namespace StandardQueryOperators;
internal class GroupOverview
{
    public static void RunAllSnippets()
    {
        Console.WriteLine("Group Overview");
        OverviewSampleQuery();
        Console.WriteLine("Group Overview Method");
        OverviewSampleMethod();
    }

    private static void OverviewSampleQuery()
    {
        // <OverviewSampleQuerySyntax>
        List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];

        IEnumerable<IGrouping<int, int>> query = from number in numbers
                                                 group number by number % 2;

        foreach (var group in query)
        {
            Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:");
            foreach (int i in group)
            {
                Console.WriteLine(i);
            }
        }
        // </OverviewSampleQuerySyntax>
    }

    private static void OverviewSampleMethod()
    {
        // <OverviewSampleMethodSyntax>
        List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];

        IEnumerable<IGrouping<int, int>> query = numbers
            .GroupBy(number => number % 2);

        foreach (var group in query)
        {
            Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:");
            foreach (int i in group)
            {
                Console.WriteLine(i);
            }
        }
        // </OverviewSampleMethodSyntax>
    }
}

author: BillWagner ms.author: wiwagn ms.topic: include ms.date: 10/04/2024

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/DataSources.csQueryDataSource

csharp
namespace StandardQueryOperators;

//<QueryDataSource>
public enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

public class Student
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required int ID { get; init; }

    public required GradeLevel Year { get; init; }
    public required List<int> Scores { get; init; }

    public required int DepartmentID { get; init; }
}

public class Teacher
{
    public required string First { get; init; }
    public required string Last { get; init; }
    public required int ID { get; init; }
    public required string City { get; init; }
}

public class Department
{
    public required string Name { get; init; }
    public int ID { get; init; }

    public required int TeacherID { get; init; }
}
// </QueryDataSource>

public static class Sources
{
    public static IEnumerable<Department> Departments =>
    [
        new() { Name = "English",     ID = 1, TeacherID = 901 },
        new() { Name = "Mathematics", ID = 2, TeacherID = 965 },
        new() { Name = "Engineering", ID = 3, TeacherID = 932 },
        new() { Name = "Economics",   ID = 4, TeacherID = 945 },
        new() { Name = "Physics",     ID = 5, TeacherID = 987 },
        new() { Name = "Chemistry",   ID = 6, TeacherID = 901 }
    ];

    // Create a data source by using a collection initializer.
    public static IEnumerable<Student> Students =>
    [
        new() { FirstName = "Svetlana",   LastName = "Omelchenko",  DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 111, Scores = [97, 90, 73, 54] },
        new() { FirstName = "Claire",     LastName = "O'Donnell",   DepartmentID = -1, Year = GradeLevel.FirstYear,  ID = 112, Scores = [56, 78, 95, 95] },
        new() { FirstName = "Sven",       LastName = "Mortensen",   DepartmentID =  3, Year = GradeLevel.SecondYear, ID = 113, Scores = [61, 52, 48, 72] },
        new() { FirstName = "Cesar",      LastName = "Garcia",      DepartmentID =  4, Year = GradeLevel.SecondYear, ID = 114, Scores = [71, 86, 77, 97] },
        new() { FirstName = "Debra",      LastName = "Garcia",      DepartmentID =  5, Year = GradeLevel.ThirdYear,  ID = 115, Scores = [66, 96, 70, 69] },
        new() { FirstName = "Fadi",       LastName = "Fakhouri",    DepartmentID =  6, Year = GradeLevel.ThirdYear,  ID = 116, Scores = [93, 72, 62, 65] },
        new() { FirstName = "Hanying",    LastName = "Feng",        DepartmentID =  1, Year = GradeLevel.FourthYear, ID = 117, Scores = [53, 81, 81, 50] },
        new() { FirstName = "Hugo",       LastName = "Garcia",      DepartmentID =  2, Year = GradeLevel.FourthYear, ID = 118, Scores = [68, 91, 60, 51] },
        new() { FirstName = "Lance",      LastName = "Tucker",      DepartmentID = -1, Year = GradeLevel.FirstYear,  ID = 119, Scores = [83, 42, 68, 63] },
        new() { FirstName = "Terry",      LastName = "Adams",       DepartmentID =  4, Year = GradeLevel.SecondYear, ID = 120, Scores = [63, 91, 71, 51] },
        new() { FirstName = "Eugene",     LastName = "Zabokritski", DepartmentID =  5, Year = GradeLevel.ThirdYear,  ID = 121, Scores = [56, 40, 73, 75] },
        new() { FirstName = "Michael",    LastName = "Tucker",      DepartmentID =  3, Year = GradeLevel.FourthYear, ID = 122, Scores = [85, 82, 81, 70] },
        new() { FirstName = "Mark",       LastName = "Johansson",   DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 123, Scores = [82, 94, 84, 65] },
        new() { FirstName = "Sarah",      LastName = "Andersson",   DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 124, Scores = [77, 83, 67, 90] },
        new() { FirstName = "Christiane", LastName = "Jensen",      DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 125, Scores = [88, 57, 65, 87] },
        new() { FirstName = "Evgeniya",   LastName = "Maslova",     DepartmentID =  2, Year = GradeLevel.SecondYear, ID = 126, Scores = [46, 84, 87, 66] },
        new() { FirstName = "Innocenty",  LastName = "Popov",       DepartmentID =  6, Year = GradeLevel.SecondYear, ID = 127, Scores = [92, 45, 88, 60] },
        new() { FirstName = "Anna",       LastName = "Hedlund",     DepartmentID =  2, Year = GradeLevel.SecondYear, ID = 128, Scores = [85, 54, 74, 75] },
        new() { FirstName = "Erik",       LastName = "Axelsson",    DepartmentID =  3, Year = GradeLevel.ThirdYear,  ID = 129, Scores = [51, 78, 54, 49] },
        new() { FirstName = "Anna",       LastName = "Yermolayeva", DepartmentID =  3, Year = GradeLevel.ThirdYear,  ID = 130, Scores = [46, 73, 93, 68] },
        new() { FirstName = "Cassie",     LastName = "Hicks",       DepartmentID =  3, Year = GradeLevel.ThirdYear,  ID = 131, Scores = [85, 60, 85, 56] },
        new() { FirstName = "Ifeanacho",  LastName = "Jamuike",     DepartmentID =  4, Year = GradeLevel.FourthYear, ID = 132, Scores = [54, 98, 56, 61] },
        new() { FirstName = "Carmen",     LastName = "Vella",       DepartmentID =  6, Year = GradeLevel.FourthYear, ID = 133, Scores = [63, 57, 69, 70] },
        new() { FirstName = "Noel",       LastName = "Svensson",    DepartmentID =  4, Year = GradeLevel.FourthYear, ID = 134, Scores = [85, 60, 80, 73] },
        new() { FirstName = "Sanna",      LastName = "Hansson",     DepartmentID =  5, Year = GradeLevel.FirstYear,  ID = 135, Scores = [46, 94, 93, 45] },
        new() { FirstName = "Sofiya",     LastName = "Seleznyova",  DepartmentID =  6, Year = GradeLevel.FirstYear,  ID = 136, Scores = [74, 45, 87, 55] },
        new() { FirstName = "Amy E.",     LastName = "Edwards",     DepartmentID =  5, Year = GradeLevel.FirstYear,  ID = 137, Scores = [87, 59, 55, 70] },
        new() { FirstName = "Nancy",      LastName = "Engström",    DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 138, Scores = [75, 73, 78, 83] },
        new() { FirstName = "Katerina",   LastName = "Kovaleva",    DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 139, Scores = [44, 50, 47, 41] },
        new() { FirstName = "Ifunanya",   LastName = "Ugomma",      DepartmentID =  4, Year = GradeLevel.FirstYear,  ID = 140, Scores = [84, 82, 96, 80] },
        new() { FirstName = "Don",        LastName = "Richardson",  DepartmentID =  5, Year = GradeLevel.FirstYear,  ID = 141, Scores = [47, 91, 73, 68] },
        new() { FirstName = "Josephine",  LastName = "Balzan",      DepartmentID =  6, Year = GradeLevel.SecondYear, ID = 142, Scores = [40, 47, 63, 42] },
        new() { FirstName = "Michelle",   LastName = "Caruana",     DepartmentID = -1, Year = GradeLevel.FirstYear,  ID = 143, Scores = [97, 92, 69, 77] },
        new() { FirstName = "Gaby",       LastName = "Frost",       DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 144, Scores = [70, 79, 47, 79] },
        new() { FirstName = "Erna",       LastName = "Nilsson",     DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 145, Scores = [56, 52, 51, 51] },
        new() { FirstName = "Naima",      LastName = "Larsson",     DepartmentID =  4, Year = GradeLevel.ThirdYear,  ID = 146, Scores = [65, 81, 44, 61] },
        new() { FirstName = "Donald",     LastName = "Urquhart",    DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 147, Scores = [92, 90, 95, 57] },
        new() { FirstName = "Nadezhda",   LastName = "Kolpakova",   DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 148, Scores = [94, 69, 52, 58] },
        new() { FirstName = "Ruth",       LastName = "Olsson",      DepartmentID =  2, Year = GradeLevel.FourthYear, ID = 149, Scores = [66, 49, 82, 74] },
        new() { FirstName = "Maria",      LastName = "Sammut",      DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 150, Scores = [43, 83, 94, 60] },
        new() { FirstName = "Veronika",   LastName = "Berg",        DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 151, Scores = [59, 76, 65, 92] },
        new() { FirstName = "Nicholas",   LastName = "Micallef",    DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 152, Scores = [44, 57, 52, 63] },
        new() { FirstName = "Izuchukwu",  LastName = "Adaobi",      DepartmentID =  4, Year = GradeLevel.SecondYear, ID = 153, Scores = [51, 40, 42, 54] },
        new() { FirstName = "Nwanneka",   LastName = "Ifeoma",      DepartmentID =  4, Year = GradeLevel.FirstYear,  ID = 154, Scores = [44, 70, 98, 56] },
        new() { FirstName = "John",       LastName = "Falzon",      DepartmentID =  5, Year = GradeLevel.FirstYear,  ID = 155, Scores = [77, 65, 83, 45] },
        new() { FirstName = "Martina",    LastName = "Mattsson",    DepartmentID =  5, Year = GradeLevel.ThirdYear,  ID = 156, Scores = [51, 49, 96, 72] },
        new() { FirstName = "Jeanette",   LastName = "Berggren",    DepartmentID =  4, Year = GradeLevel.FirstYear,  ID = 157, Scores = [41, 67, 46, 68] },
        new() { FirstName = "Anastasiya", LastName = "Sazonova",    DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 158, Scores = [53, 96, 76, 49] },
        new() { FirstName = "Bruce",      LastName = "Keever",      DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 159, Scores = [54, 81, 84, 81] },
        new() { FirstName = "Sami",       LastName = "Åkesson",     DepartmentID =  5, Year = GradeLevel.FourthYear, ID = 160, Scores = [45, 85, 79, 94] },
        new() { FirstName = "Jesper",     LastName = "Jakobsson",   DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 161, Scores = [59, 98, 47, 92] },
        new() { FirstName = "Max",        LastName = "Lindgren",    DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 162, Scores = [86, 88, 96, 63] },
        new() { FirstName = "Arina",      LastName = "Ivanova",     DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 163, Scores = [93, 63, 70, 80] }
    ];

    public static IEnumerable<Teacher> Teachers =>
    [
        new() { First = "Ann",       Last = "Beebe",     ID = 901, City = "Seattle" },
        new() { First = "Alex",      Last = "Robinson",  ID = 910, City = "Redmond" },
        new() { First = "Michiyo",   Last = "Sato",      ID = 921, City = "Tacoma"  },
        new() { First = "Aylin",     Last = "Lundgren",  ID = 932, City = "Seattle" },
        new() { First = "Gleb",      Last = "Seleznyov", ID = 943, City = "Redmond" },
        new() { First = "Alexandra", Last = "Voronova",  ID = 954, City = "Tacoma" },
        new() { First = "Kevin",     Last = "McDowell",  ID = 965, City = "Redmond" },
        new() { First = "Selma",     Last = "Nyberg",    ID = 976, City = "Seattle" },
        new() { First = "Selma",     Last = "Åstrom",    ID = 987, City = "Redmond" },
        new() { First = "Hillevi",   Last = "Bengtsson", ID = 998, City = "Tacoma" },
        new() { First = "Allan",     Last = "Sandberg",  ID = 912, City = "Seattle" },
        new() { First = "Dmitry",    Last = "Degtyarev", ID = 923, City = "Redmond" },
        new() { First = "Arinze",    Last = "Madu",      ID = 934, City = "Seattle" },
        new() { First = "Andrey",    Last = "Glazkov",   ID = 945, City = "Redmond" },
        new() { First = "Kettil",    Last = "Berggren",  ID = 956, City = "Tacoma" },
        new() { First = "Hanying",   Last = "Feng",      ID = 967, City = "Seattle" },
        new() { First = "Hugo",      Last = "Garcia",    ID = 978, City = "Redmond" },
        new() { First = "Michael",   Last = "Tucker",    ID = 989, City = "Tacoma" },
        new() { First = "Ifeanacho", Last = "Jamuike",   ID = 991, City = "Redmond" },
        new() { First = "Carmen",    Last = "Vella",     ID = 982, City = "Tacoma" },
        new() { First = "Noel",      Last = "Svensson",  ID = 973, City = "Seattle" }
   ];
}

author: '' ms.author: '' ms.topic: include ms.date: 08/11/2025

Group query results

Grouping is one of the most powerful capabilities of LINQ. The following examples show how to group data in various ways:

  • By a single property.
  • By the first letter of a string property.
  • By a computed numeric range.
  • By Boolean predicate or other expression.
  • By a compound key.

In addition, the last two queries project their results into a new anonymous type that contains only the student's first and family name. For more information, see the group clause.

Group by single property example

The following example shows how to group source elements by using a single property of the element as the group key. The key is an enum, the student's year in school. The grouping operation uses the default equality comparer for the type.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByPropertyQuery

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

The equivalent code using method syntax is shown in the following example:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByPropertyMethod

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

Group by value example

The following example shows how to group source elements by using something other than a property of the object for the group key. In this example, the key is the first letter of the student's family name.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByValueQuery

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

Nested foreach is required to access group items.

The equivalent code using method syntax is shown in the following example:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByValueMethod

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

Group by a range example

The following example shows how to group source elements by using a numeric range as a group key. The query then projects the results into an anonymous type that contains only the first and family name and the percentile range to which the student belongs. An anonymous type is used because it isn't necessary to use the complete Student object to display the results. GetPercentile is a helper function that calculates a percentile based on the student's average score. The method returns an integer between 0 and 10.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByRangeQuery

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

Nested foreach required to iterate over groups and group items. The equivalent code using method syntax is shown in the following example:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByRangeMethod

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

Group by comparison example

The following example shows how to group source elements by using a Boolean comparison expression. In this example, the Boolean expression tests whether a student's average exam score is greater than 75. As in previous examples, the results are projected into an anonymous type because the complete source element isn't needed. The properties in the anonymous type become properties on the Key member.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByBooleanQuerySyntax

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

The equivalent query using method syntax is shown in the following code:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByBooleanMethodSyntax

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

Group by anonymous type

The following example shows how to use an anonymous type to encapsulate a key that contains multiple values. In this example, the first key value is the first letter of the student's family name. The second key value is a Boolean that specifies whether the student scored over 85 on the first exam. You can order the groups by any property in the key.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByCompundKeyQuerySyntax

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

The equivalent query using method syntax is shown in the following code:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/GroupQueryResults.csGroupByCompundKeyMethodSyntax

csharp
namespace StandardQueryOperators;

public class GroupQueryResults
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Group ByProperty Results");
        GroupByPropertyQuery();
        Console.WriteLine("Group ByProperty Results Method");
        GroupByPropertyMethod();
        Console.WriteLine("Group ByValue Results");
        GroupByValueQuery();
        Console.WriteLine("Group ByValue Results Method");
        GroupByValueMethod();
        Console.WriteLine("Group ByRange Results");
        GroupByRangeQuery();
        Console.WriteLine("Group ByRange Results Method");
        GroupByRangeMethod ();
        Console.WriteLine("Group ByBoolean Results");
        GroupByBooleanQuery();
        Console.WriteLine("Group ByBoolean Results Method");
        GroupByBooleanMethod();
        Console.WriteLine("Group ByCompoundKey Results");
        GroupByCompoundKeyQuery();
        Console.WriteLine("Group ByCompoundKey Results Method");
        GroupByCompoundKeyMethod();
    }

    public static void GroupByPropertyQuery()
    {
        // <GroupByPropertyQuery>
        var groupByYearQuery =
            from student in students
            group student by student.Year into newGroup
            orderby newGroup.Key
            select newGroup;

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyQuery>
    }

    public static void GroupByPropertyMethod()
    {
        // <GroupByPropertyMethod>
        // Variable groupByYearQuery is an IEnumerable<IGrouping<GradeLevel,
        // DataClass.Student>>.
        var groupByYearQuery = students
            .GroupBy(student => student.Year)
            .OrderBy(newGroup => newGroup.Key);

        foreach (var yearGroup in groupByYearQuery)
        {
            Console.WriteLine($"Key: {yearGroup.Key}");
            foreach (var student in yearGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByPropertyMethod>
    }

    public static void GroupByValueQuery()
    {
        // <GroupByValueQuery>
        var groupByFirstLetterQuery =
            from student in students
            let firstLetter = student.LastName[0]
            group student by firstLetter;

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }

        // </GroupByValueQuery>
    }

    public static void GroupByValueMethod()
    {
        // <GroupByValueMethod>
        var groupByFirstLetterQuery = students
            .GroupBy(student => student.LastName[0]);

        foreach (var studentGroup in groupByFirstLetterQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
            }
        }
        // </GroupByValueMethod>
    }

    public static void GroupByRangeQuery()
    {
        // <GroupByRangeQuery>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery =
            from student in students
            let percentile = GetPercentile(student)
            group new
            {
                student.FirstName,
                student.LastName
            } by percentile into percentGroup
            orderby percentGroup.Key
            select percentGroup;

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }

        // </GroupByRangeQuery>
    }

    public static void GroupByRangeMethod()
    {
        // <GroupByRangeMethod>
        static int GetPercentile(Student s)
        {
            double avg = s.Scores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        var groupByPercentileQuery = students
            .Select(student => new { student, percentile = GetPercentile(student) })
            .GroupBy(student => student.percentile)
            .Select(percentGroup => new
            {
                percentGroup.Key,
                Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
            })
            .OrderBy(percentGroup => percentGroup.Key);

        foreach (var studentGroup in groupByPercentileQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key * 10}");
            foreach (var item in studentGroup.Students)
            {
                Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
            }
        }
        // </GroupByRangeMethod>
    }

    public static void GroupByBooleanQuery()
    {
        // <GroupByBooleanQuerySyntax>
        var groupByHighAverageQuery =
            from student in students
            group new
            {
                student.FirstName,
                student.LastName
            } by student.Scores.Average() > 75 into studentGroup
            select studentGroup;

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanQuerySyntax>
    }

    public static void GroupByBooleanMethod()
    {
        // <GroupByBooleanMethodSyntax>
        var groupByHighAverageQuery = students
            .GroupBy(student => student.Scores.Average() > 75)
            .Select(group => new
            {
                group.Key,
                Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
            });

        foreach (var studentGroup in groupByHighAverageQuery)
        {
            Console.WriteLine($"Key: {studentGroup.Key}");
            foreach (var student in studentGroup.Students)
            {
                Console.WriteLine($"\t{student.FirstName} {student.LastName}");
            }
        }
        // </GroupByBooleanMethodSyntax>
    }

    public static void GroupByCompoundKeyQuery()
    {
        // <GroupByCompundKeyQuerySyntax>
        var groupByCompoundKey =
            from student in students
            group student by new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            } into studentGroup
            orderby studentGroup.Key.FirstLetterOfLastName
            select studentGroup;

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyQuerySyntax>
    }

    public static void GroupByCompoundKeyMethod()
    {
        // <GroupByCompundKeyMethodSyntax>
        var groupByCompoundKey = students
            .GroupBy(student => new
            {
                FirstLetterOfLastName = student.LastName[0],
                IsScoreOver85 = student.Scores[0] > 85
            })
            .OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);

        foreach (var scoreGroup in groupByCompoundKey)
        {
            var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
            Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
            foreach (var item in scoreGroup)
            {
                Console.WriteLine($"\t{item.FirstName} {item.LastName}");
            }
        }
        // </GroupByCompundKeyMethodSyntax>
    }
}

Create a nested group

The following example shows how to create nested groups in a LINQ query expression. Each group that is created according to student year or grade level is then further subdivided into groups based on the individuals' names.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/NestedGroups.csNestedGroupsQuerySyntax

csharp
namespace StandardQueryOperators;

public static class NestedGroups
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Nested Group Query Syntax");
        NestedGroupQuery();
        Console.WriteLine("Nested Group Method Syntax");
        NestedGroupMethod();
    }

    private static void NestedGroupQuery()
    {
        // <NestedGroupsQuerySyntax>
        var nestedGroupsQuery =
            from student in students
            group student by student.Year into newGroup1
            from newGroup2 in
            from student in newGroup1
            group student by student.LastName
            group newGroup2 by newGroup1.Key;

        foreach (var outerGroup in nestedGroupsQuery)
        {
            Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}");
            foreach (var innerGroup in outerGroup)
            {
                Console.WriteLine($"\tNames that begin with: {innerGroup.Key}");
                foreach (var innerGroupElement in innerGroup)
                {
                    Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}");
                }
            }
        }
        // </NestedGroupsQuerySyntax>
    }
    private static void NestedGroupMethod()
    {
        // <NestedGroupsMethodSyntax>
        var nestedGroupsQuery =
            students
            .GroupBy(student => student.Year)
            .Select(newGroup1 => new
            {
                newGroup1.Key,
                NestedGroup = newGroup1
                    .GroupBy(student => student.LastName)
            });

        foreach (var outerGroup in nestedGroupsQuery)
        {
            Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}");
            foreach (var innerGroup in outerGroup.NestedGroup)
            {
                Console.WriteLine($"\tNames that begin with: {innerGroup.Key}");
                foreach (var innerGroupElement in innerGroup)
                {
                    Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}");
                }
            }
        }
        // </NestedGroupsMethodSyntax>
    }
}

Three nested foreach loops are required to iterate over the inner elements of a nested group.
(Hover the mouse cursor over the iteration variables, outerGroup, innerGroup, and innerGroupElement to see their actual type.)

The equivalent query using method syntax is shown in the following code:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/NestedGroups.csNestedGroupsMethodSyntax

csharp
namespace StandardQueryOperators;

public static class NestedGroups
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Nested Group Query Syntax");
        NestedGroupQuery();
        Console.WriteLine("Nested Group Method Syntax");
        NestedGroupMethod();
    }

    private static void NestedGroupQuery()
    {
        // <NestedGroupsQuerySyntax>
        var nestedGroupsQuery =
            from student in students
            group student by student.Year into newGroup1
            from newGroup2 in
            from student in newGroup1
            group student by student.LastName
            group newGroup2 by newGroup1.Key;

        foreach (var outerGroup in nestedGroupsQuery)
        {
            Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}");
            foreach (var innerGroup in outerGroup)
            {
                Console.WriteLine($"\tNames that begin with: {innerGroup.Key}");
                foreach (var innerGroupElement in innerGroup)
                {
                    Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}");
                }
            }
        }
        // </NestedGroupsQuerySyntax>
    }
    private static void NestedGroupMethod()
    {
        // <NestedGroupsMethodSyntax>
        var nestedGroupsQuery =
            students
            .GroupBy(student => student.Year)
            .Select(newGroup1 => new
            {
                newGroup1.Key,
                NestedGroup = newGroup1
                    .GroupBy(student => student.LastName)
            });

        foreach (var outerGroup in nestedGroupsQuery)
        {
            Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}");
            foreach (var innerGroup in outerGroup.NestedGroup)
            {
                Console.WriteLine($"\tNames that begin with: {innerGroup.Key}");
                foreach (var innerGroupElement in innerGroup)
                {
                    Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}");
                }
            }
        }
        // </NestedGroupsMethodSyntax>
    }
}

Perform a subquery on a grouping operation

This article shows two different ways to create a query that orders the source data into groups, and then performs a subquery over each group individually. The basic technique in each example is to group the source elements by using a continuation named newGroup, and then generating a new subquery against newGroup. This subquery is run against each new group created by the outer query. In this particular example the final output isn't a group, but a flat sequence of anonymous types.

For more information about how to group, see group clause. For more information about continuations, see into. The following example uses an in-memory data structure as the data source, but the same principles apply for any kind of LINQ data source.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SubqueryOnGroup.csSubQueryOnGroupQuerySyntax

csharp
namespace StandardQueryOperators;

public class SubqueryOnGroup
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Subquery on Group");
        SubqueryOnGroupQuery();
        Console.WriteLine("Subquery on Group Method");
        SubqueryOnGroupMethod();
    }

    private static void SubqueryOnGroupQuery()
    {
        // <SubQueryOnGroupQuerySyntax>
        var queryGroupMax =
            from student in students
            group student by student.Year into studentGroup
            select new
            {
                Level = studentGroup.Key,
                HighestScore = (
                    from student2 in studentGroup
                    select student2.Scores.Average()
                ).Max()
            };

        var count = queryGroupMax.Count();
        Console.WriteLine($"Number of groups = {count}");

        foreach (var item in queryGroupMax)
        {
            Console.WriteLine($"  {item.Level} Highest Score={item.HighestScore}");
        }

        // </SubQueryOnGroupQuerySyntax>
    }

    public static void SubqueryOnGroupMethod()
    {
        // <SubQueryOnGroupMethodSyntax>
        var queryGroupMax =
            students
                .GroupBy(student => student.Year)
                .Select(studentGroup => new
                {
                    Level = studentGroup.Key,
                    HighestScore = studentGroup.Max(student2 => student2.Scores.Average())
                });

        var count = queryGroupMax.Count();
        Console.WriteLine($"Number of groups = {count}");

        foreach (var item in queryGroupMax)
        {
            Console.WriteLine($"  {item.Level} Highest Score={item.HighestScore}");
        }
        // </SubQueryOnGroupMethodSyntax>
    }
}

The query in the preceding snippet can also be written using method syntax. The following code snippet has a semantically equivalent query written using method syntax.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/SubqueryOnGroup.csSubQueryOnGroupMethodSyntax

csharp
namespace StandardQueryOperators;

public class SubqueryOnGroup
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Subquery on Group");
        SubqueryOnGroupQuery();
        Console.WriteLine("Subquery on Group Method");
        SubqueryOnGroupMethod();
    }

    private static void SubqueryOnGroupQuery()
    {
        // <SubQueryOnGroupQuerySyntax>
        var queryGroupMax =
            from student in students
            group student by student.Year into studentGroup
            select new
            {
                Level = studentGroup.Key,
                HighestScore = (
                    from student2 in studentGroup
                    select student2.Scores.Average()
                ).Max()
            };

        var count = queryGroupMax.Count();
        Console.WriteLine($"Number of groups = {count}");

        foreach (var item in queryGroupMax)
        {
            Console.WriteLine($"  {item.Level} Highest Score={item.HighestScore}");
        }

        // </SubQueryOnGroupQuerySyntax>
    }

    public static void SubqueryOnGroupMethod()
    {
        // <SubQueryOnGroupMethodSyntax>
        var queryGroupMax =
            students
                .GroupBy(student => student.Year)
                .Select(studentGroup => new
                {
                    Level = studentGroup.Key,
                    HighestScore = studentGroup.Max(student2 => student2.Scores.Average())
                });

        var count = queryGroupMax.Count();
        Console.WriteLine($"Number of groups = {count}");

        foreach (var item in queryGroupMax)
        {
            Console.WriteLine($"  {item.Level} Highest Score={item.HighestScore}");
        }
        // </SubQueryOnGroupMethodSyntax>
    }
}

See also