Understanding the Enumerable.Aggregate method in LINQ
Tags: dotnet, beginners, tutorial, csharp
Published: 2022-08-07 08:44:00

**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:

```csharp
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
```

**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:
```csharp

//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"
```

**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:
```csharp
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);
```

- 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](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods) of `IEnumerable`.
- `source` is the collection we are aggregating over.
- `Func<TSource, TSource, TSource>` is a [delegate](https://docs.microsoft.com/en-us/dotnet/api/system.func-2?view=net-6.0) 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:

```csharp
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
```

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.

```csharp

//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: 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.

```csharp
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
```

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](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.aggregate?view=net-6.0) for this method.

Happy coding. 🚀