Linq Aggregate Empty Sequence

Linq is great for its compact expressions, and for its — usually efficient — lazy-evaluation. These two virtues seem to be spit upon by the Aggregate function.

Say you want to aggregate a list of ints.

var s = new int[] {1, 2, 3};
int sum = ints.Aggregate((a,b) => a + b);

This works great if your list has elements in it, or they aren’t filtered out in a where clause. Say you had a function that took a list of ints and someone passed that list of 1,2,3 to your function:

int SumOverTen(IEnumerable<int> ints)
{
int agg = ints.Where(i => i > 10).Aggregate((a,b)=> a+b);
return agg;
}

Everything now goes pear-shaped and you get an InvalidOperationException “Sequence contains no elements”.

So what do you do to fix this? You could rewrite your function to check for an empty result first:

int SumOverTen(IEnumerable<int> ints)
{
int agg = 0;
var bigints = ints.Where(i => i > 10);
if(bigints.Count() > 0)
agg = bigints.Aggregate((a,b)=> a+b);

return agg;
}

This makes a nice Linq expression into a nasty branching mess. The list will now be enumerated twice, once for Count and once for Aggregate.

Well, it turns out that there is a better way, using Linq:

int SumOverTen(IEnumerable<int> ints)
{
int agg = ints.Where(i => i > 10)
.DefaultIfEmpty()
.Aggregate((a,b)=> a+b);

return agg;
}

This results in the same value as the previous function but is much more legible and throws no exceptions on an empty list. However, I don’t know how DefaultIfEmpty checks for an empty list so it may not be more efficient than the previous code.

What I want to know is, why does Aggregate throw instead of returning the same result as using DefaultIfEmpty?

This entry was posted in Software Development, Technology and tagged , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

6 Comments

  1. Joseph Vannucci
    Posted April 21, 2010 at 6:41 am | Permalink

    Good find on this. I’ve found a lot of “check the count on the list” answers all over the interwebs.

    Thanks!

  2. Kostya
    Posted November 25, 2010 at 3:13 am | Permalink

    Great idea….
    From my point of view – it must be default behavior of Aggregate functions
    How did you find this?

  3. dz
    Posted August 25, 2011 at 12:04 am | Permalink

    Nice solution. Thanks a lot

  4. tim314
    Posted October 26, 2011 at 2:26 am | Permalink

    You can Aggregate an empty list *if* you specify the initial value. Aggregate has an overload for this.

    In this case, instead of using .DefaultIfEmpty(), you can just replace
    .Aggregate((a,b)=> a+b)
    with
    .Aggregate(0, (a,b)=> a+b)

  5. Ahe
    Posted December 16, 2011 at 4:24 am | Permalink

    In this case this does not work, but if reasonable default value can be given following solves this issue:


    public int Sum
    {
    get { return numbers.Aggregate(0, (result, val) => result += val);
    }

  6. Romain
    Posted October 17, 2012 at 6:59 am | Permalink

    Very nice,
    FYI, DefaultIfEmpty() doesn’t loop twice.
    Here is the implementation:

    // System.Linq.Enumerable
    private static IEnumerable DefaultIfEmptyIterator(IEnumerable source, TSource defaultValue)
    {
    using (IEnumerator enumerator = source.GetEnumerator())
    {
    if (enumerator.MoveNext())
    {
    do
    {
    yield return enumerator.Current;
    }
    while (enumerator.MoveNext());
    }
    else
    {
    yield return defaultValue;
    }
    }
    yield break;
    }

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>