При работе с последовательностями (списками) элементов бывает необходимым сгруппировать эти элементы по определенному признаку. Для этих целей в LINQ имеется метод расширения GroupBy
.
Метод GroupBy
Допустим, у нас имеется набор данных по измерениям какой-то величины в различное время суток:
public class DataObject { public string Name { get; set; } public double Value { get; set; } public override string ToString() { return $"{Name} {Value}"; } }
static void Main(string[] args) { List<DataObject> objects = new List<DataObject>() { new DataObject {Name = "Утро", Value = 12.5 }, new DataObject {Name = "День", Value = 12.1 }, new DataObject {Name = "Вечер", Value = 80.5 }, new DataObject {Name = "Ночь", Value = 11.3 }, new DataObject {Name = "Утро", Value = 10.7 }, new DataObject {Name = "День", Value = 12.1 }, new DataObject {Name = "Вечер", Value = 80.5 }, new DataObject {Name = "Вечер", Value = 11.3 } }; }
Нам необходимо сгруппировать все элементы списка по времени суток. Используя метод расширения LINQ GroupBy
, это можно сделать следующим образом:
var result = objects.GroupBy(o=>o.Name); foreach (IGrouping<string, DataObject> datas in result) { Console.WriteLine($"Группа: {datas.Key}"); IEnumerable<DataObject> groupElements = datas.Take(datas.Count()); foreach (DataObject data in groupElements) Console.WriteLine(data.ToString()); Console.WriteLine(); }
Результат выполнения этого кода будет следующим:
Утро 12,5
Утро 10,7
Группа: День
День 12,1
День 12,1
Группа: Вечер
Вечер 80,5
Вечер 80,5
Вечер 11,3
Группа: Ночь
Ночь 11,3
Метод GroupBy
проводит группировку по заданному с помощью параметра критерию. В нашем случае — это была группировка по полю Name
: var result = objects.GroupBy(o=>o.Name)
Результатом работы метода GroupBy
является коллекция типа IEnumerable<IGrouping<string, object>>
то есть — это список групп, полученных в результате выполнения метода GroupBy
. string
— определяет ключ для группы (в нашем случае — это значение свойства Name
объекта), а object
— тип сгруппированных данных (в нашем случае — это DataObject
)
Таким образом, для того, чтобы получить сведения по каждой группе мы использовали два цикла foreach: первый цикл проходит по всем группам, а второй — по элементам внутри каждой группы.
Как и любые другие методы расширения LINQ, метод GroupBy
можно использовать в связке с другими методами.
Совместное использование GroupBy и Select
Метод Select
мы рассматривали, когда разбирались с тем, что такое проекции в LINQ и как они создаются. Рассмотрим следующий пример со списком, представленным выше:
var result = objects.GroupBy(o => o.Name).Select(n => new { Name = n.Key, Count = n.Count() }); foreach (var group in result) { Console.WriteLine($"{group.Name}: Количество элементов {group.Count}"); }
Здесь мы на основании группировки списка по полю Name
создали новую проекцию — коллекцию объектов, каждый из которых содержит два поля: имя группы и количество элементов в списке. Результат выполнения этого кода будет следующим:
День: Количество элементов 2
Вечер: Количество элементов 3
Ночь: Количество элементов 1
Метод ToLookup
Метод ToLookup также может использоваться для группировки элементов последовательности по ключу, но при этом все элементы добавляются в словарь. Чтобы продемонстрировать различие между GrouBy
и ToLookup
воспользуемся этим методом и также сгруппируем все элементы списка, представленного выше по ключу Name
.
ILookup<string, DataObject> res = objects.ToLookup(o => o.Name); Console.WriteLine($"Количество групп {res.Count}"); foreach (IGrouping<string, DataObject> element in res) { Console.WriteLine($"Группа {element.Key}. Количество элементов: {element.Count()}"); foreach (DataObject data in element) { Console.WriteLine(data.ToString()); } Console.WriteLine(); }
Результат выполнения будет следующим:
Группа Утро. Количество элементов: 2
Утро 12,5
Утро 10,7
Группа День. Количество элементов: 2
День 12,1
День 12,1
Группа Вечер. Количество элементов: 3
Вечер 80,5
Вечер 80,5
Вечер 11,3
Группа Ночь. Количество элементов: 1
Ночь 11,3
Объект, реализующий интерфейс ILookup
, возвращаемый в результате выполнения метода ToLookup
содержит свойство Count
— количество групп, а также метод Contains
, определяющий содержится ли группа с определенным названием (ключом) в полученной коллекции.
Итого
Сегодня мы рассмотрели способ группировки элементов последовательности в LINQ по определенному признаку. Результатом выполнения метода GroupBy
в LINQ является коллекция групп. Используя метод Select
также можно создавать свои проекции элементов, полученной последовательности.