Processing collections with LINQ using the Aggregate extension method

A 3 minute read, Posted on Wed, Sep 14, 2011 In Coding
Tags .net, linq

Everyone knows one of the easiest ways to process collections of objects is using foreach, it is much more elegant and less error prone (IMHO) than using a for (int x..) and looping by index.  However, sometimes you get forced into using the indexer to process a collection.  This usually happens when you need to look at the current object in context of the previous.  

The need to use the indexer and have code like obj[i] == obj[i -1] bugged me. I spent a little time digging into the LINQ extensions to see if anything could help.  Then i stumbled upon an explanation of the various aggregation methods that got me thinking.  After a little play time in Linq Pad I had a working solution.

I discovered that the answer was in using the Aggregate method.  The Aggregate method takes a seed value and a function that accepts two paramters and returns a value, all of the type of the collection.  The first call in passes the seed value and the current item, each subsiquent call passes the previous calls output and the current item.  Using this we are able to seed with a null and return the "current" item untouched, allowing continuous processing of the current value againts the previous (or seed) value.

Here is my sample code, it processes the list and counts the number of times each property toggles into the true state (and ignores contiguous true values):

 

public class Item
{
    public bool Value1 { get; set; }
    public bool Value2 { get; set; }
}

void Main()
{
    var Items = new List<Item>()
    {
        new Item() { Value1 = true, Value2 = false },
        new Item() { Value1 = true, Value2 = false },
        new Item() { Value1 = true, Value2 = true },
        new Item() { Value1 = true, Value2 = true },
        new Item() { Value1 = false, Value2 = true },
        new Item() { Value1 = false, Value2 = true },
        new Item() { Value1 = true, Value2 = false },
        new Item() { Value1 = true, Value2 = false },
    };
        //Number of Times Value1 entered the on state.
    int count1 = 0; 
        //Number of Times Value2 entered the on state.
    int count2 = 0;

    Items.Aggregate (null, (Func<Item, Item, Item>)((a, b)=>
    {
        count1 += ((a == null || a.Value1 != b.Value1) && b.Value1 == true ? 1 : 0);
        count2 += ((a == null || a.Value2 != b.Value2) && b.Value2 == true ? 1 : 0);
        return b;
    }));
}

I hope you find this helpful.  Obviously you could have an actual method defined to do this processing istead of an anonymous Func, but I like having this simple code inline with local variables.

comments powered by Disqus