Advanced Basics: Bringing it in with LINQ's Aggregate in C#

June 26th, 2019

In today's post, I show you how to tame and understand LINQ's Aggregate method.

Since LINQ appeared on the scene in 2007, it's changed the way we look at arrays, lists, and data access. LINQ gives us filtering (.Where), sorting (.OrderBy), and calculating (.Sum) capabilities.

However, there are some LINQ methods that aren't as well-known as the more common LINQ methods I mentioned above.

One of those methods is the Aggregate method.

Personally, I don't use .Aggregate very often, but I'm guessing others don't use it as frequently as well. I thought some may find it interesting to see examples of this powerful method (and possibly other unknown methods in the near future).

Aggregate

The Aggregate method is more of a custom LINQ method where you can achieve any type of custom aggregation over a list of items.

Of course, there are already existing LINQ methods to assist with specialized collecting.

For example, if you need to add a group of numbers, use the .Sum() method.

Looking for the smallest number in the list? Use the .Min() method.

Biggest number? Use .Max().

Each one of these are a form of the .Aggregate() method. However, if you need something customizable, the .Aggregate() method is your best bet. Where this method shines is when you need to concatenate strings in a certain way.

The Aggregate method has a number of overloaded options to easily build data types with Enumerables.

Overload 1

The first overloaded method is the simplest one. It uses an accumulator over an enumerable type.

This can pertain to a list of char, list of numbers, or a List of strings. Anything you can loop through can be used in the Aggregate method.

For example, if want to take a list and create a comma-delimited string, we could do it using one of two ways: the first example is using an Aggregate and the second example is using the long way.

    public static class StringExtensions
    {
        public static string QuotedStr(this string str)
        {
            return "\"" + str + "\"";
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<string> { "The""Quick""Brown""Fox""Jumped""Over""The""Lazy""Dog" };
 
            // 1 - First Aggregate
            var csv = list.Aggregate((resultnextItem=>
                {
                    if (result == list.First())
                    {
                        result = result.ToString().QuotedStr();
                    }
                    return result + ", " + nextItem.ToString().QuotedStr();
                }
            );
 
            // 1b - Long way of doing it.
            var sb = "";
            foreach (var item in list)
            {
                sb += item.QuotedStr();
                if (item != list.Last())
                {
                    sb += ", ";
                }
            }
 
            Console.WriteLine("Aggregate 1");
            Console.WriteLine(csv);
            Console.WriteLine(sb.ToString());
  
            Console.ReadKey();
        }
    }

The obvious benefit to using the Aggregate method is the length of code.

Overload 2

If you need a seed value for your TAccumulate, the best thing to use is the second signature of this method.

The difference with this signature is it starts off with a seed value called TAccumulate. This is an initializer which will essentially become your result.

This value can be an integer (0 to start with) or a string (""). Once this accumulator is looping through your enumerable, it will add onto the seed value.

int[] ints = { 4, 8, 8, 3, 9, 0, 7, 8, 2 };

// Count the even numbers in the array, using a seed value of 0.
int numEven = ints.Aggregate(0, (total, next) =>     next % 2 == 0 ? total + 1 : total);

Console.WriteLine("The number of even integers is: {0}", numEven);
// This code produces the following output:
// // The number of even integers is: 6
Console
.ReadKey();

As you can see, we could easily use .Sum() to get the total of all of the numbers in the loop, but if you want a custom way of calculating numbers or strings, the .Aggregate() method is your best bet.

Overload 3

Now, we come to our final method signature. This one is a bit of a handful, but don't let it intimidate you.

The first parameter is our seed value. Again, this can be any data type, but it's primarily string or int.

The second parameter is the function for processing the element in the list.

The third parameter is the function for returning the result.

It's better to show an example.

string[] fruits = {"apple", "mango", "orange", "passionfruit", "grape"};

// Determine whether any string in the array is longer than "banana".
string longestName =     fruits.Aggregate("banana",         (longest, next) =>             next.Length > longest.Length ? next : longest,         // Return the final result as an upper case string.         fruit => fruit.ToUpper());
Console
.WriteLine(     "The fruit with the longest name is {0}.",     longestName);
// This code produces the following output:
// // The fruit with the longest name is PASSIONFRUIT.
Console
.ReadKey();

The first parameter is the seed value of a string called "banana."

The second parameter uses a Function as we said above. In this Func, we could have three parameters, but we only have two parameters passed in at this time.

To explain the code, the "longest" parameter is held onto as the seed value. The "next" parameter is the next element in the enumerable list.

If the length of the "next" value is longer than the "longest" value, it will return "next" as the "longest" value and set "next" as the "longest" on the next time the loop gets to the next item.

The third parameter is meant for tidying up and returning the result. As you can see, we make the result uppercase.

Conclusion

There are so many ways to approach the .Aggregate() method since it's so customizable.

However, if the .Aggregate() method becomes too unwieldy, I would recommend taking that complicated method and turn it into something a little more maintainable, like an extension method.

What tips do you have for the .Aggregate() method? Do you use it a lot? How is it handy for your needs? Post your comments below and let's discuss.