Fun with LINQ Aggregate
Say we’ve got a CSV file:
private const string csv =
@"1,2,3,4,5
6,7,8,9,10
11,12,13,14,15
16,17,18,19,20";
We want to parse it into nested List<int>, and then print out all the numbers on a single line. We might do it like this:
public void ParseCsvAndOutputAsString()
{
var data = new List<List<int>>();
foreach (var line in csv.Split('\n'))
{
var innerList = new List<int>();
foreach (var item in line.Split(','))
{
innerList.Add(int.Parse(item));
}
data.Add(innerList);
}
string output = "";
foreach (var innerList in data)
{
foreach (var item in innerList)
{
output = string.Format("{0} {1}", output, item);
}
}
Console.WriteLine(output);
}
Yes, I know I should use a StringBuilder and AddRange, but ignore that for the moment, I’m trying to make a point here.
Taking a collection of values and reducing them down to a single value is a very common task in programming. Here we’re doing it twice; first we’re taking a string, splitting it apart and then reducing it down to a single reference to a List<List<int>>; then we’re taking the may items of data and reducing them to a string.
This is so common in fact, that many programming languages have some kind of ‘reduce’ functionality built in. It’s especially common with functional languages. Did you know that C# also has a reduce function? It’s the Aggregate extension method. Here’s the same method written in two statements with it:
[Test]
public void ParseCsvAndOutputAsStringUsingAgregate()
{
var data = csv
.Split('\n')
.Aggregate(
new List<List<int>>(),
(list, line) => list.Append(line
.Split(',')
.Select(str => int.Parse(str))
.Aggregate(
new List<int>(),
(innerList, item) => innerList.Append(item))));
Console.WriteLine(data
.SelectMany(innerList => innerList)
.Aggregate("", (output, item) => string.Format("{0} {1}", output, item)));
}
Aggregate takes two parameters; the first sets up the initial value, in our case we create new instances of List<List<int>>, List<int> and an empty string, this is known as the ‘accumulator’; the second is the function that does the accumulating.
Aggregate works really well with fluent interfaces, where methods return their instance. I’ve added a fluent ‘Append’ extension method to List<int> to help me here:
public static List<T> Append<T>(this List<T> list, T item)
{
list.Add(item);
return list;
}
So any time you’ve got a collection of stuff that you want to ‘reduce’ to a single item, remember Aggregate.
- Login or register to post comments
- 2012 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)



