LINQ в C#: группировка элементов последовательности

При работе с последовательностями (списками) элементов бывает необходимым сгруппировать эти элементы по определенному признаку. Для этих целей в 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
День: Количество элементов 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();
}

Результат выполнения будет следующим:

Количество групп 4
Группа Утро. Количество элементов: 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 также можно создавать свои проекции элементов, полученной последовательности.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии