Tuesday, March 08, 2011

Adding the sequence number to a LINQ query

LINQ queries are a powerful way to keep your code expressive and by using differed execution, they are quick. But what if you need the value of the index which LINQ used whilst building the projection? I had this issue and found the solution was to use the Select method which accepts a Func<TSource, int, TResult> for the selector.

With a for loop this is simple to accomplish as the index value is available in each iteration as it is controlling the loop:
This code prints this to the console:

Creating a LINQ query with the indexer in the projection is not as obvious. My first attempt was to use the Count() property of the line parameter in the projection.

But when run to the console the problem became apparent

In the projection, line.Count() is returning the string length of each line in the array. First attempts are always a good way to discover how something could work

Fortunately the LINQ Select Method has two overloads. They both iterate over an IEnumerable<T> but it is the delegate for the selector which differs. The code above uses the first delegate which should be Func<TSource, TResult>. The second overload expects a Func<TSource, int, TResult>. Here, the parameter used for the int32 is assigned the current value for the index of the sequence.

The first code example can now be changed to something more expressive

Running this code displays the following in the console

Many of the LINQ methods have this overload for the selector. Using it I have been able to continue using LINQ to specify how I want to transform the array. This means my code is more expressive. Plus, the LINQ query is quick as the projection will only be populated when the foreach loop is executed to display the result.