LINQ Farm Seed I: Aggregate Operator Part I
LINQ Farm Seeds are short posts designed to be read in a few minutes. The first seed showed how to list in alphabetical order all the operators used in LINQ to Objects. At the top of the list one found the Aggregate operator.
The LINQ Aggregation operator is easy to use. Consider the following declaration:
List<int> ints = new List<int> { 1, 2, 3 };
The aggregate of the numbers in this list would be 1 + 2 + 3 or 6. Consider this array:
List<int> ints = new List<int> { 1, 2, 3, 4 };
The aggregate of the numbers in this list would be 10:
10 = 1 + 2 + 3 + 4;
To perform this kind of calculation one could take the first two numbers in the list, add them together to get 3, then add the third number to their sum to get 6, then add the fourth number that sum to get 10:
- 3 = 1 + 2
- 6 = 3 + 3
- 10 = 6 + 4
This algorithm is used by the first of the three overrides of the LINQ to Objects Aggregate operator. Using typical LINQ syntax, one could call the operator as follows:
int sum = integers.Aggregate((a, b) => a + b);
The Aggregate operator is passed a simple lambda expression that takes two parameters and adds them together. In our case, using the List<int> shown above, the first two parameters would be 1 and 2 and the return value would be 3. The function derived from the lambda expression would then be called again with the value 3 and 3, and so on.
If you are not yet quite comfortable using lambdas, you could accomplish the same ends with the following code:
private int AddEm(int a, int b)
{
return a + b;
}
int sum = integers.Aggregate(AddEm);
This code also compiles and runs correctly. It is semantically equivalent to the lambda expression code shown above it. The word lambda sounds complicated, but in some ways lambda expressions are even easier to create and understand than a method such as AddEm().
A complete program implementing the Aggregate operator is shown in Listing 1.
Listing 1: A short LINQ program that outputs the number 10.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int count = 5;
List<int> integers = new List<int>();
for (int i = 0; i < count; i++)
{
integers.Add(i);
}
int sum = integers.Aggregate((a, b) => a + b);
Console.WriteLine(sum);
}
}
}
It would be a bit more fun to use this program if would could dynamically change the value of count. To make this possible I have created a simple Windows Form application. You can download this implementation from the LINQ Farm on Code Gallery. A screen shot from the application is shown in Figure 1.
Figure 01: The aggregate of 0 + 1 + 2 + 3 + 4 is 10.
Here is the declaration for this first override of the Aggregate operator:
public static TSource Aggregate<TSource>(this IEnumerable<TSource> source,
Func<TSource, TSource, TSource> func);
Since it is an extension method, the first parameter to this generic method is effectively ignored. As a result, we need only be concerned with the second parameter, which looks like this:
Func<TSource, TSource, TSource> func
Assuming we are passing in an array of integers to this LINQ operator, then this declaration states that Func takes two integers as parameters and returns an integer:
int Func(int a, int b)
If you look up above at the AddEm method, you will see that it has the same signature. The lambda expression we have been using also generates code with the same signature:
(a, b) => a + b;
In this short post you have seen how to work with the first override of the Aggregate operator. In future posts, I will show how to work with other overrides of this same operator.
---
Down the source code from the LINQ Farm.
I want to get my hands dirty; show me more LINQ Farm posts.
Comments
Anonymous
February 05, 2008
PingBack from http://www.travel-hilarity.com/airline_travel/?p=1239Anonymous
February 05, 2008
I left this additional posting on a friend's blog. I love the 3rd overload on Aggrigate. return list.Aggregate(new StringBuilder(), (sb, n) => sb.Length == 0 ? sb.Append(n) : sb.Append(",").Append(n), sb => sb.ToString()); More details here: http://www.31a2ba2a-b718-11dc-8314-0800200c9a66.com/2008/02/04/CommaDelimitedStringFromAnArrayUsingLINQ.aspxAnonymous
February 05, 2008
If I understand how this works from your description, then the following are common aggregations as well using the same overload: int min = integers.Aggregate((a, b) => Math.Min(a, b)) int max = integers.Aggregate((a, b) => Math.Max(a, b)) I don't have 2008 or 3/3.5 frameworks loaded yet so I could be wrong. Also what happens if the list is only one unit long? Shouldn't the int aggregate start from 0 (default value) and item 1. Then taking that aggregate and the next value in the list return the new aggregate and so on. Also what happens if the list was 0 length? Does it return 0 the default int value or null because there is nothing to do? Returning null is an issue if we are dealing with ints though.Anonymous
February 05, 2008
WarePhreak, the min and max examples you gave work fine - very useful. If the list is only one unit long, it still seems to work. The addition aggregate just gives the value of the single list entry. The Min and Max aggregations also just give the value of the single list entry. If the list is empty, a System.InvalidOperationException is thrown, with additional information of 'Sequence contains no elements'. So you'll either have to catch the exception or check the length of the list before calling the Aggregate.Anonymous
February 06, 2008
LINQ Farm Seeds are short posts designed to be read in a few minutes. In the previous seed we used theAnonymous
February 06, 2008
Charlie Calvert's LINQ Farm SeedsAnonymous
February 10, 2008
Charlie Calvert is a C# Community Evanelist at Microsoft and his blog is filled with informative postsAnonymous
February 14, 2008
> Also what happens if the list is only one unit long? It returns that element (with the overload looked at here, the other overloads return the seed if zero length). > return list.Aggregate(new StringBuilder(), I think that the need for two function calls can make this look obscure, and given the simple overload's handling of one length lists an approach more like below is simpler: list.Select(x => x.ToString()).Aggregate((x,y) => x + "," + y);