Понимание метода Enumerable.Aggregate в LINQ

TL/DR

  • метод Aggregate является методом расширения коллекций, которые наследуются от IEnumerable
  • метод возвращает только одно значение любого типа
  • Это функция накопления, такая же как sum, count и т.д.
  • имеет 3 перегрузки

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

Это отличается от суммирования всех денег, которые будут списаны со счета, потому что:

  • есть условие, которое должно выполняться в каждой итерации списка счетов
  • возвращаемое значение представляет собой кортеж, т.е. кортеж из трех значений int, представляющих отчисления за неделю, месяц и год.

В данном случае лучше всего использовать метод Aggregate. Он присутствует во всех коллекциях C#, которые наследуются от IEnumerable.

Метод Aggregate выполняет операцию над каждым элементом списка, принимая во внимание предыдущие операции. Например, он выполняет операцию над первыми двумя элементами списка и использует результат для операции над третьим элементом и так далее, чтобы вернуть одно значение.

Рассмотрим следующее:

int[] numbers = { 1, 2, 3, 4, 5};

//we use aggregate to get the summation of the elements of the list

int summation = numbers.Aggregate((a, b) => a + b);

//summation will be 1 + 2 + 3 + 4 + 5 = 15
Войти в полноэкранный режим Выход из полноэкранного режима

Функция накопления

Метод Aggregate является функцией накопления. Функция накопления объединяет строки в коллекции, выполняет операцию над каждой из них и возвращает одно значение. В LINQ существуют различные функции накопления: Sum, Max, Min, Count и Average, но метод Aggregate может выполнять все операции, которые выполняют эти методы, и даже больше.

Еще одно преимущество метода Aggregate заключается в том, что он выполняет операции над коллекцией любого типа, в отличие от других методов, которые работают только с числовыми типами данных.

Рассмотрим следующее:


//flatten a list of countries to a string separated by comma

string[] countries = {"Nigeria", "Ghana", "Togo"};

string countriesToString = countries.Aggregate((a, b) => a + ", " + 
  b);

//countriesToString will be "Nigeria, Ghana, Togo"
Войдите в полноэкранный режим Выход из полноэкранного режима

Перегрузки метода агрегирования

Метод Aggregate имеет три перегрузки, первая из которых описана и использована выше.
Однако здесь я приведу сигнатуру первой перегрузки и объясню ее в терминах C#.
Три перегрузки следующие:

1. public static TSource Aggregate<TSource>(this IEnumerable<TSource> source,  
Func<TSource, TSource, TSource> func);

2. public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source,  
TAccumulate seed,  
Func<TAccumulate, TSource, TAccumulate> func);

3. public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source,  
TAccumulate seed,  
Func<TAccumulate, TSource, TAccumulate> func,  
Func<TAccumulate, TResult> resultSelector);
Войти в полноэкранный режим Выйти из полноэкранного режима
  • Метод aggregate является общим, то есть он принимает и возвращает любой тип, отсюда Aggregate<TSource> и другие.

То же самое относится и к другим перегрузкам. Она представляет операцию накопления, которая будет вызвана над каждым элементом коллекции.

  • Вторая и третья перегрузки имеют параметр под названием seed, т.е. TAccumulate seed.

Он представляет собой начальное значение аккумулятора. Оно может быть любого типа, это означает, что «начните с этого значения».

Значение этого семени определяет возвращаемое значение Aggregate.

Давайте воспользуемся примером с числами, приведенным выше.
Допустим, мы хотим добавить 10 к сумме перед добавлением других элементов, вот что мы сделаем:

int[] numbers = {1, 2, 3, 4, 5};

//we use the seed parameter:

int summation = numbers.Aggregate(10, (a, b) => a + b);

//summation will be 10 + 1 + 2 + 3 + 4 + 5 = 25
Войти в полноэкранный режим Выйти из полноэкранного режима

Последняя перегрузка имеет дополнительный делегат Func<TAccumulate, TResult> resultSelector, который представляет собой функцию для преобразования конечного значения в нужное нам значение результата.

Это означает, что поскольку значение Aggregate возвращает только одно значение, мы можем решить выполнить другую операцию над полученным результатом.

Давайте проверим это на примере наших стран.


//flatten a list of countries to a string separated by comma
//add another west African country to the start of the list
//then convert these countries to upper case

string[] countries = {"Nigeria", "Ghana", "Togo"};

string countriesToUpper = countries.Aggregate("Liberia", (a, b) => a + ", " + 
  b, countryString => countryString.ToUpper());

//countriesToUpper will be "LIBERIA, NIGERIA, GHANA, TOGO"
Войти в полноэкранный режим Выйти из полноэкранного режима

NB: Есть лучший способ написать приведенный выше код. Мы можем преобразовать страны в верхний регистр без использования этой перегрузки.

Вместо использования лямбды мы можем определить метод вне блока кода и использовать его внутри метода Aggregator.

int[] numbers = {1, 2, 3, 4, 5};

//we use the seed parameter:

int result =  numbers.Aggregate(0, (a, b) =>
{
    return doMathsOnNumbers(a, b);
});

int doMathsOnNumbers(int first, int second)
{
    if (first > second)
        return first - second;
    return first + second;
};
//result will be 7
Вход в полноэкранный режим Выйти из полноэкранного режима

Этот метод очень интересен, и вам стоит его опробовать. Однажды он обязательно пригодится.
Подробнее об этом методе вы можете прочитать в документации Microsoft.

Счастливого кодирования. 🚀

Оцените статью
devanswers.ru
Добавить комментарий