System.Linq.Enumerable.Aggregate – Better Know an Extension Method Part 1

As apart of this series I will be investigating the Extension methods made available in the System.Linq namespace specifically for classes that implement IEnumerable. If you have any gems you have created for the extension method in question drop a comment below as I would love to see what people are coming up with.

The first extension method investigated is Aggregate the function documented as:

“Applies an accumulator function over a sequence.”

The following code is the simple program and data class I will be using to demonstrate the power of Aggregate! Please note if you are follow along line 19 is were the test cases are placed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Entry
{
    public int Id { get; set; }
    public string Name { get; set; }
}
 
class Program
{
    static void Main(string[] args)
    {
        var items = new List<Entry>()
        {
            new Entry() {Id = 1, Name = "Horse"},
            new Entry() {Id = 2, Name = "Dog"},
            new Entry() {Id = 3, Name = "Cat"},
            new Entry() {Id = 4, Name = "Pig"}
        };
 
        // This is were our test cases go!
 
        Console.ReadLine();
    }
}

Test case 1: Aggregate<(Of <(TSource>)>)(IEnumerable<(Of <(TSource>)>), Func<(Of <(TSource, TSource, TSource>)>))

First of all TSource is the strongly typed class the Enumeration contains so if your using List<string> your TSource is a string or if your using a List<MyObject> your TSource is a MyObject. The second important part is Func<(Of <(TSource, TSource, TSource>)> this says that aggregate will take a delegate that provides two TSources and returns one TSource following is an example of such a function:

Func<string, string, string> stupidFunction = (a, b) => b;

This really simple example takes two strings and returns one. Now we will look at the simplest of all 3 overloads available the only requirement is a lambda expression that returns the same type as the strongly typed Enumeration provides identified as TSource in the method signature.

19
20
21
22
23
24
25
var entry = items.Aggregate((a, b) =>
{
    a.Name += b.Name;
    return a;
});
Console.WriteLine(entry.Name);
Console.WriteLine(items[0].Name);

HorseDogCatPig
HorseDogCatPig

As you can see we have modified the Entry with the Id of 1 to contain an Aggregate of all the names, to avoid changing the first Entry we must introduce the idea of a Seed.

Test case 2: Aggregate<(Of <(TSource, TAccumulate>)>)(IEnumerable<(Of <(TSource>)>), TAccumulate, Func<(Of <(TAccumulate, TSource, TAccumulate>)>))

The seed we pass in will be the object we recieve as the result of calling Aggregate the same instance is passed into every call to the expression. The following uses the aggregate without modifying the Enumeration.

19
20
21
22
23
24
25
var entry = items.Aggregate(new Entry(), (a, b) => 
{
    a.Name += b.Name;
    return a;
});
Console.WriteLine(entry.Name);
Console.WriteLine(items[0].Name);

HorseDogCatPig
Horse

Another neat trick to try with Aggregate is to pass in a different kind of seed like. While the change is very suttle it has the effect of changing the return type to a StringBuilder and no longer an Entry so we must now call ToString instead of Name.

19
20
21
var entry = items.Aggregate(new StringBuilder(), (a, b) => a.Append(b.Name) );
Console.WriteLine(entry.ToString());
Console.WriteLine(items[0].Name);

HorseDogCatPig
Horse

Test case 3: Aggregate<(Of <(TSource, TAccumulate, TResult>)>)(IEnumerable<(Of <(TSource>)>), TAccumulate, Func<(Of <(TAccumulate, TSource, TAccumulate>)>), Func<(Of <(TAccumulate, TResult>)>))

The last overload gives you one more expression to play with which is run once all the items have been enumerated over, how you want to use this will depend on what your doing but the following just appends “. DONE!” to the end of the StringBuilder.

19
20
21
var entry = items.Aggregate(new StringBuilder(), (a, b) => a.Append(b.Name), x => x.Append(". DONE!"));
Console.WriteLine(entry.ToString());
Console.WriteLine(items[0].Name);

HorseDogCatPig. DONE!
Horse

Test case 4: Real life scenario
Goal: Add up all the orders and provide a rounded total.

class Order
{
    public double Total { get; set; }
}
 
class Program
{
    static void Main(string[] args)
    {
        var orders = new List<Order>()
         {
             new Order() {Total = 123.23},
             new Order() {Total = 512},
             new Order() {Total = 123.23},
             new Order() {Total = 5172.34},
             new Order() {Total = 647.518},
             new Order() {Total = 368.180},
             new Order() {Total = 12.84},
             new Order() {Total = 947.25},
         };
 
        var roundedGrandTotal = orders.Aggregate(new double(), 
            (total, order) => total += order.Total,
            (total) => Math.Round(total, 2));
 
        Console.WriteLine("{0:c}",roundedGrandTotal);
        Console.ReadLine();
    }
}

Result

$7,906.59

And thats it! I hope I have shed light on this extremely useful extension method which can be used for anything you dream up. This is my first article on .NET for this blog so please leave some feedback. System.Linq.Enumerable.All- Better Know an Extension Method Part 2

12 Responses to “System.Linq.Enumerable.Aggregate – Better Know an Extension Method Part 1”


  • Interesting… I didn’t know about that one. I can think of lots of places where that would come in handy. Thanks for the tip!

  • Jon, care to share any scenarios where this would come in handy? The biggest question I get is when/how should I use these extension methods.

  • Great article, so many hidden gems in those extension methods on IEnumerable.

    Keep up the great work!

  • @Chris: There are lots of use cases for Aggregate: The most obvious are of course logical/arithmetic operations. Enumerable does have a Sum extension method, but with Aggregate you can also easily calculate the product of a list of numbers, or the sum of the squares, or an xor-checksum.
    (Actually, Aggregate is a lot more general than that: Almost any list processing can be simplified to a fold aka Enumberable.Aggregate. Unfortunately, Linq has no built-in equivalent for Cons, so it’s harder to exploit the general power of Aggregate to build new lists as result.)

    BTW: I think you’re not using it the way it’s meant:
    var roundedGrandTotal = orders.Aggregate(new double(),
    (total, order) => total += order.Total,
    (total) => Math.Round(total, 2));

    In the second parameter “(total, order) => total += order.Total”, the “total” parameter of the lambda expression is not a mutable variable. It’s a by-value parameter, and it’s value is lost as soon as it goes out of scope. Your code only works because += has a value. This would be clearer:
    var roundedGrandTotal = orders.Aggregate(new double(),
    (total, order) => total + order.Total,
    (total) => Math.Round(total, 2));

  • Note that in Test Case 3 you can change the result type in the last overload. So instead of x => x.Append(“DONE”) you can do x => x.ToString(), and not have to worry about the string builder object except in the scope of the Aggregate function.

    This same technique could be used in the last example to convert the double to decimal.

  • @J thanks I will look at this some more and update the article.

  • @Niki didnt really think of it in the way of a fold, what do you mean by con?

  • Sorry, the part about folds was a little cryptic: What I meant was that Enumerable.Aggregate is equivalent to the operation called “left fold” in functional programming (http://en.wikipedia.org/wiki/Fold_(higher-order_function) ).

    In functional programming, folding is often used to build complex data structures like lists: You define a function called “Cons” that builds a linked list, so if nil is the empty list Cons(1, nil) would be the list containing only the element 1, Cons(1, Cons(2, nil)) would be the list containing 1,2 and so on. Then you can do things like (in .NET syntax):
    myList.Aggregate( (nextElement, list) => Cons(nextElement, list), nil);
    or shorter:
    myList.Aggregate(Cons, nil);
    Which would just duplicate the list. But you can do any kind of processing in the fold, e.g. you might check for a condition:
    myList.Aggregate( (nextElement, list) => someCondition(nextElement) ? Cons(nextElement, list) : list, nil);
    which filters a list like “Enumerable.Where”.

  • The aggregate function expression is also part of the F# functional language. Other places it is useful is when doing things like a weighted average on the fly for records bound in a data grid control or doing statistical calculations or business intelligence calcs. Back in the .NET 1.x days this was such a pain in the ass (as I didn’t understand delegates back then well enough to be dangerous).

  • It’s surprising just how much of a productivity boost advances like this give us.

    Nice tip!

  • Is this only available in vs 2008 or could I use this functionality in vs 2005 and just include the System.Core?

Leave a Reply