BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

LINQ
.NET 3.5+

Projecting Indexes with LINQ

All sequences have a notional order that can be expressed using index values in a similar manner to the indexes of an array. When projecting sequences using LINQ's Select and SelectMany operators, this index value can be included in the projection.

Projection

Language-Integrated Query (LINQ) provides several standard query operators that can be used to project a sequence. This is where you apply a function, usually provided as a lambda expression, to each element in a sequence and generate a new list containing the projected items. You can perform this action using the Select or SelectMany operators, or using LINQ's query expression syntax.

Index Projection

You can think of any sequence as having an order. This may be the natural order in which the elements just happen to appear or a specified order when a collection is sorted. In an array, the order is obvious due to the index values, which start at zero for the first item and increment for each subsequent element. Although other collection types may not explicitly include a zero-based index, you can include one in projections using the Select or SelectMany extension methods.

We can see this by first looking at a simple projection. In the sample code below, an array of strings is projected into a new sequence using the Select operator. The lambda expression specifies that each element should be capitalised. The lambda's single parameter, "n", represents each of the source array's elements during the operation.

var ordinals = new string[] { "First", "Second", "Third" };
var caps = ordinals.Select(n => n.ToUpper());

/* RESULTS

FIRST
SECOND
THIRD

*/

To project the index value, the lambda expression is altered to include a second parameter. Now the first parameter represents the source element and the second, integer parameter is the incrementing index. In the code below, the ordinals array is projected to a sequence of anonymously typed values, each containing properties for the original value and the index number.

var indexed = ordinals.Select((n, i) => new { Index = i, Ordinal = n });

/* RESULTS

{ Index = 0, Ordinal = "First" }
{ Index = 1, Ordinal = "Second" }
{ Index = 2, Ordinal = "Third" }

*/

SelectMany

When you perform one-to-many projection using the SelectMany operator you can use the same technique. Here the index number represents the index of the outer sequence. This means that the combined inner sequences can include multiple items with the same outer index.

The code below demonstrates this. Here we have a main list containing two inner lists. The SelectMany operator allows all of the elements from the inner lists to be combined and projected into a single sequence. By using the second parameter on the SelectMany operator's lambda we project the index of the outer list into each resultant element The index and the inner lists' values are finally combined into longer strings.

var seq1 = new List<string> { "One", "Two", "Three" };
var seq2 = new List<string> { "Four", "Five", "Six" };
var mainSequence = new List<List<string>> { seq1, seq2 };
var numbers = mainSequence.SelectMany((g, i) => g.Select(n => i.ToString() + " " + n));

/* RESULTS

0 One
0 Two
0 Three
1 Four
1 Five
1 Six

*/
24 May 2011