Pile of stones

LINQ Aggregate Method Explained with Pictures

This is not one of the most used LINQ methods. We won’t use it every day. But, it’s handy for some scenarios. Let’s learn how to use the Aggregate method.

The Aggregate method applies a function on a collection carrying the result to the next element. It “aggregates” the result of a function over a collection.

The Aggregate method takes two parameters: a seed and an aggregating function that takes the accumulated value and one element from the collection.

How does Aggregate work?

Let’s reinvent the wheel to understand Aggregate by finding the maximum rating in our movie catalog. Of course, LINQ has a Max method. And, .NET 6 introduced new LINQ methods, among those: MaxBy.

var movies = new List<Movie>
{
    new Movie("Titanic", 1998, 4.5f),
    new Movie("The Fifth Element", 1997, 4.6f),
    new Movie("Terminator 2", 1991, 4.7f),
    new Movie("Avatar", 2009, 5),
    new Movie("Platoon", 1986, 4),
    new Movie("My Neighbor Totoro", 1988, 5)
};

var maxRating = movies.Aggregate(0f, (maxSoFar, movie) => MaxBetween(maxSoFar, movie.Rating));
//                     ^^^^^^^^^
Console.WriteLine($"Maximum rating on our catalog: {maxRating}");

// Output:
// Comparing 0 and 4.5
// Comparing 4.5 and 4.6
// Comparing 4.6 and 4.7
// Comparing 4.7 and 5
// Comparing 5 and 4
// Comparing 5 and 5
// Maximum rating on our catalog: 5

Console.ReadKey();

float MaxBetween(float maxSoFar, float rating)
{
    Console.WriteLine($"Comparing {maxSoFar} and {rating}");
    return rating > maxSoFar ? rating : maxSoFar;
}

record Movie(string Name, int ReleaseYear, float Rating);

Notice we used Aggregate() with two parameters: 0f as the seed and the delegate (maxSoFar, movie) => MaxBetween(maxSoFar, movie.Rating) as the aggregating function. maxSoFar is the accumulated value from previous iterations, and movie is the current movie while Aggregate iterates over our list. The MaxBetween() method returns the maximum between two numbers.

Notice the order of the debugging messages we printed every time we compare two ratings in the MaxBetween() method.

On the first iteration, the Aggregate() method executes the MaxBetween() aggregating function using the seed (0f) and the first element (“Titanic” with 4.5) as parameters.

Aggregate first iteration on a list of movies
Aggregate first iteration

Next, it calls MaxBetween() with the previous result (4.5) as the maxSoFar and the next element of the collection (“The Fifth Element” with 4.6f).

Aggregate second iteration on a list of movies
Aggregate second iteration

In the last iteration, Aggregate() finds the maxSoFar from all previous iterations and the last element (“My Neighbor Totoro” with 5). And it returns the last value of maxSoFar as a result.

Aggregate last iteration on a list of movies
Aggregate last iteration

In our example, we used Aggregate() with a seed. But, Aggregate() has an overload without it, then it uses the first element of the collection as the seed. Also, Aggregate() has another parameter to transform the result before returning it.

Voilà! That’s how the Aggregate method works. Remember, it returns an aggregated value from a collection instead of another collection. This is one of those methods we don’t use often. I’ve used it only a couple of times. One of them was in my parsing library, Parsinator, to apply a list of modification functions on the same input object here.

If you want to read more about LINQ and its features, check my quick guide to LINQ, five common LINQ mistakes and how to fix them and what’s new in LINQ with .NET6.

Want to write more expressive code for collections? Join my course, Getting Started with LINQ on Udemy! You'll learn from what LINQ is, to refactoring away from conditionals, and to new methods and overloads from recent .NET versions. Everything you need to know to start working productively with LINQ — in less than two hours.

Happy coding!