DEV Community

Isaac Ayodeji Ikusika
Isaac Ayodeji Ikusika

Posted on • Edited on

Understanding the Enumerable.Aggregate method in LINQ

TL/DR

  • the Aggregate method is an extension method of collections that inherit from IEnumerable
  • the method returns only one value of any type
  • It is an accumulation function same as sum, count etc.
  • it has 3 overloads

Some weeks ago, I had to display the expected total amount of money to be debited from a list of account in a specific year, month and week, starting from the first day the account was added to the system, based on a condition.

This is different from the summation of all the money to be debited from the account, because:

  • there is a condition to be followed in each iteration of the list of accounts
  • my return value is a tuple i.e. a tuple of three int values representing week, month and year deductions

The preferred method to use in this case is the Aggregate method. It is present in all C# collections that inherits from IEnumerable.

The Aggregate method performs an operation on each element in a list, taking the previous operations into account. For example, it performs an operation on the first two elements of a list and uses the result to operate on the third element and on and on to return a single value.

Consider this:

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
Enter fullscreen mode Exit fullscreen mode

Accumulation Function

The Aggregate method is an accumulation function. An accumulation function combines the rows in a collection, performs an operation on each one of them and returns a single value. There are different accumulation functions in LINQ: Sum, Max, Min, Count and Average, but the Aggregate method can perform all the operations these methods perform and more.

One other advantage of Aggregate is that it performs operations on a collection of any type, unlike others that only work with numerical data types.

Consider this:


//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"
Enter fullscreen mode Exit fullscreen mode

Overloads of the Aggregate Method

The Aggregate method has three overloads, the first one is explained and used above.
However, I will add the signature of the first overload here and explain in C# terms.
The three overloads are:

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);
Enter fullscreen mode Exit fullscreen mode
  • The aggregate method is generic, meaning it takes and returns any type, hence, the Aggregate<TSource> and others.
  • TSource here is the type of the elements of the Enumerable we are aggregating over. The this keyword here shows that the Aggregate function is an extension method of IEnumerable.
  • source is the collection we are aggregating over.
  • Func<TSource, TSource, TSource> is a delegate that returns the last TSource.

This is the same for other overloads. It represents the accumulator operation to be invoked on each element of the collection.

  • The second and third overloads have a parameter called seed i.e. TAccumulate seed.

This represents the initial accumulator value. It could be any type, it is saying, "start with this value".

The value of this seed determines the return value of the Aggregate

Let us use our numbers example from above.
Let's say we want to add 10 to the sum before adding other elements, this is what we do:

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
Enter fullscreen mode Exit fullscreen mode

The last overload has an extra delegate Func<TAccumulate, TResult> resultSelector, which is a function to transform the final value into the result value we want.

It means since the Aggregate value returns only one value, we can decide to perform another operation on the result we got.

Let's practice this with our countries example from above.


//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"
Enter fullscreen mode Exit fullscreen mode

NB: There is a better way to write the code above. We can convert the countries to upper case without the use of this overload.

Instead of using a lambda, we can use define a method outside the code block and use it inside the Aggregator method.

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
Enter fullscreen mode Exit fullscreen mode

This method is fun and you should try it out. It sure will come handy one of these days.
You can read more on the Microsoft docs for this method.

Happy coding. 🚀

Top comments (0)