LINQ Farm Seed 02: Aggregate Operator Part II

LINQ Farm Seeds are short posts designed to be read in a few minutes. In the previous seed we used the first overload of the C# 3.0 LINQ Aggregate operator to sum a list of numbers. In this post we'll work with a list of words.

The code we will look at reverses the following string:

  • The end is the beginning, the beginning the end

If we pass this string in to our method, it will yield this result:

  • end the beginning the beginning, the is end The

Here is the method that we will use:

 private string ReverseSentence(string sentence)
{
  string[] words = sentence.Split(' ');           

  return words.Aggregate((accumulator, b) => b + " " + accumulator);
}

The method first breaks the sentence down into a list of words:

  string[] words = sentence.Split(' ');

It passes this list to the LINQ to Objects Aggregate operator. This overload of that operator takes a single lambda expression as a parameter. The lambda expression takes two parameters (accumulator, b) and returns them in reverse order (b accumulator). If it were a standard method rather than a lambda expression, it would look like this:

 private static string Reverse(string accumulator, string b)
{
   return b + " " + accumulator;
}

The method would be called like this:

  words.Aggregate(Reverse);

In our code, however, we use lambda expressions rather than standard methods.

As you recall from the previous post, the Aggregate operators passes in items two at a time from a list. The first parameter becomes the accumulator for the aggregation. This is why I chose to give the first parameter in the lambda expression the name accumulator:

return words.Aggregate((accumulator, b) => b + " " + accumulator);

You can watch the aggregation process take place in the debugger. Set a breakpoint on the call to Aggregate, and add the variables accumulator and b to your watch list. Step through the method with the F11 key. You can watch the result "accumulate" in the accumulator variable.

The process begins by passing the first two words into the lambda expression, where the first one is the accumulator:

  • (The) (end)

The method reverses their order and places a space between them:

  • end The

The next iteration sends in the result of the previous expression, plus the next word from the list:

  • (end the) (is)

The lambda expression reverses them:

  • is end The

The Aggregate operator now processes the result of the previous iteration in the accumulator plus the next word:

  • (is end The) (the)

Again our lambda expression swaps the parameters and places a space between them:

  • the is end The

Next time we pass in the following:

  • (the is end The) (beginning,)

This yields:

  • beginning, the is end The

The rest of the cycle looks like this:

  • (beginning, the is end The), (the) >===> the beginning, the is end The
  • (the beginning, the is end The) (beginning) >===> beginning the beginning, the is end The
  • (beginning the beginning, the is end The) (the) >===> the beginning the beginning, the is end The
  • (the beginning the beginning, the is end The) (end) >===> end the beginning the beginning, the is end The

I find this an immensely satisfying process to contemplate simply because it so very logically runs counter to my intuitions. I keep expecting it to do something different, but when I think it through I see exactly why it does what it does. How many real world uses for this I will discover I don't know, but I will treasure each one as I encounter it.

Listing 1 shows the complete code for this simple program. In this version of the ReverseSentence method, you see how to convert the sentence int List<string> rather than an array of string. I use the ToList() LINQ operator to perform this conversion, so you are seeing a sneak peak of an operator I haven't yet covered in this series.

Listing 01: This code shows how to use the Aggregate() and ToList() operators that are declared in the Enumerator class and which are part of LINQ to Objects.

 using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqFarmSeed02
{
    class Program
    {
        private static string ReverseString(string sentence)
        {
            List<string> words = new List<string>(sentence.Split(' ').ToList());

            return words.Aggregate((a, b) => b + " " + a);            
        }

        static void Main(string[] args)
        {
            string sentence = "The end is the beginning, the beginning the end";
            Console.WriteLine(sentence);

            string result = ReverseString(sentence);
            Console.WriteLine(result);
        }        
    }
}

In this post you have seen how to use the Aggregate operator to reverse the order of a list of words. Thinking through the way this operator works on a list of strings can help you understand exactly how it is implemented and exactly what it does.

-----------------

Download the source code from the LINQ Farm.

I want to get my hands dirty; show me more LINQ Farm posts.

kick it on DotNetKicks.com