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.
Счастливого кодирования. 🚀