Полное руководство по языку программирования С# 14 и платформе .NET 10 / LINQ

Группировка

Начальный ⏱ 4 мин чтения 👁 2 🕓 обновлено 11.08.2026

Для группировки данных по определенным параметрам применяется оператор group by и метод GroupBy().

Оператор group by

Допустим, у нас есть набор из объектов следующего типа:


record class Person(string Name, string Company);

Данный класс представляет пользователя и имеет два свойства: Name (имя пользователя) и Company (компания, где работает пользователь). Сгруппируем набор пользователей по компании:


Person[] people =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
    new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};

var companies = from person in people
                group person by person.Company;

foreach(var company in companies)
{
    Console.WriteLine(company.Key);

    foreach(var person in company)
    {
        Console.WriteLine(person.Name);
    }
    Console.WriteLine(); // для разделения между группами
}

record class Person(string Name, string Company);

Если в выражении LINQ последним оператором, выполняющим операции над выборкой, является group, то оператор select не применяется.

Оператор group принимает критерий по которому проводится группировка:

group person by person.Company

в данном случае группировка идет по свойству Company. Результатом оператора group является выборка, которая состоит из групп. Каждая группа представляет объект IGrouping<K, V>: параметр K указывает на тип ключа - тип свойства, по которому идет группировка (здесь это тип string). А параметр V представляет тип сгруппированных объектов - в данном случае группируем объекты Person.

Каждая группа имеет ключ, который мы можем получить через свойство Key: g.Key. Здесь это будет название компании.

Все элементы внутри группы можно получить с помощью дополнительной итерации. Элементы группы имеют тот же тип, что и тип объектов, которые передавались оператору group, то есть в данном случае объекты типа Person.

В итоге мы получим следующий вывод:

Microsoft
Tom
Mike
Alice

Google
Sam

JetBrains
Bob
Kate

GroupBy

В качестве альтернативы можно использовать метод расширения GroupBy. Он имеет ряд перегрузок, возьмем самую простую из них:


GroupBy<TSource,TKey> (Func<TSource,TKey> keySelector);

Данная версия получает делегат, которые в качестве параметра принимает каждый элемент коллекции и возвращает критерий группировки.

Перепишем предыдущий пример с помощью метода GroupBy:


Person[] people =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
    new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};

var companies = people.GroupBy(p => p.Company);

foreach(var company in companies)
{
    Console.WriteLine(company.Key);

    foreach(var person in company)
    {
        Console.WriteLine(person.Name);
    }
    Console.WriteLine(); // для разделения между группами
}

record class Person(string Name, string Company);

Создание нового объекта при группировке

Теперь изменим запрос и создадим из группы новый объект:


Person[] people =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
    new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};

var companies = from person in people
                group person by person.Company into g
                select new { Name = g.Key, Count = g.Count() }; ;

foreach(var company in companies)
{
    Console.WriteLine($"{company.Name} : {company.Count}");
}

record class Person(string Name, string Company);

Выражение

group person by person.Company into g

определяет переменную g, которая будет содержать группу. С помощью этой переменной мы можем затем создать новый объект анонимного типа (хотя также можно под данную задачу определить новый класс):

select new { Name = g.Key, Count = g.Count() }

Теперь результат запроса LINQ будет представлять набор объектов таких анонимных типов, у которых два свойства Name и Count.

Результат программы:

Microsoft : 3
Google : 1
JetBrains : 2

Аналогичная операция с помощью метода GroupBy():


var companies = people
                    .GroupBy(p=>p.Company)
                    .Select(g => new { Name = g.Key, Count = g.Count() });

Вложенные запросы

Также мы можем осуществлять вложенные запросы:


Person[] people =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
    new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};
var companies = from person in people
                group person by person.Company into g
                select new
                {
                    Name = g.Key,
                    Count = g.Count(),
                    Employees = from p in g select p
                };

foreach (var company in companies)
{
    Console.WriteLine($"{company.Name} : {company.Count}");
    foreach(var employee in company.Employees)
    {
        Console.WriteLine(employee.Name);
    }
    Console.WriteLine(); // для разделения компаний
}

record class Person(string Name, string Company);

Здесь свойство Employees каждой группы формируется с помощью дополнительного запроса, который выбирает всех пользователей в этой группе. Консольный вывод программы:

Microsoft : 3
Tom
Mike
Alice

Google : 1
Sam

JetBrains : 2
Bob
Kate

Аналогичный запрос с помощью метода GroupBy:


var companies = people
                    .GroupBy(p=>p.Company)
                    .Select(g => new
                    {
                        Name = g.Key,
                        Count = g.Count(),
                        Employees = g.Select(p=> p) 
                    });

Проверь себя

📋 Тест по теме «Группировка» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

_____ class Person(string Name, string Company);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Person[] _____ =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
    new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};

var companies = from person in people
                group person by person.Company;

foreach(var company in companies)
{
    Console.WriteLine(company.Key);

    foreach(var person in company)
    {
        Console.WriteLine(person.Name);
    }
    Console.WriteLine(); // для разделения между группами
}

record class Person(string Name, string Company);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

group _____ by person.Company
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

GroupBy<TSource,TKey> (_____<TSource,TKey> keySelector);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Person[] people =
{
    new Person("_____", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
    new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};

var companies = people.GroupBy(p => p.Company);

foreach(var company in companies)
{
    Console.WriteLine(company.Key);

    foreach(var person in company)
    {
        Console.WriteLine(person.Name);
    }
    Console.WriteLine(); // для разделения между группами
}

record class Person(string Name, string Company);
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

record class _____(string _____, string Company);
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

_____[] people =
{
    _____ Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
    new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};

var companies = from person in people
                group person by person.Company;

foreach(var company in companies)
{
    Console.WriteLine(company.Key);

    foreach(var person in company)
    {
        Console.WriteLine(person.Name);
    }
    Console.WriteLine(); // для разделения между группами
}

record class Person(string Name, string Company);
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

                    .Select(g => new { Name = g.Key, Count = g.Count() });
                    .GroupBy(p=>p.Company)
var companies = people
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Группировка»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Группировка», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

record class Person(string Name, string Company);
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Группировка»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

Person[] people =
{
    new Person("Tom", "Microsoft"), new Person("Sam", "Google"),
    new Person("Bob", "JetBrains"), new Person("Mike", "Microsoft"),
    new Person("Kate", "JetBrains"), new Person("Alice", "Microsoft"),
};

var companies = from person in people
                group person by person.Company;

foreach(var company in companies)
{
    Console.WriteLine(company.Key);

    foreach(var person in company)
    {
        Console.WriteLine(person.Name);
    }
    Console.WriteLine(); // для разделения между группами
}

record class Person(string Name, string Company);
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Группировка»
+50 XP

Соберите небольшую программу, которая последовательно демонстрирует ключевые идеи урока. Программа должна быть рабочей и содержать минимум два самостоятельно написанных фрагмента, а не просто копию примера из урока.

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Группировка»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Группировка». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.