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+

A LINQ Operator to Select Multiple Values from Sequences

Language-Integrated Query (LINQ) includes several projection operators but none that allow multiple items to be created from each item in a sequence. This article describes a custom operator that allows several selections to be made per item.

Implementation

The implementation for SelectMulti is quite simple. An outer loop enumerates the source array. A nested loop then executes each selector against the current source sequence item, returning the results with a yield return statement.

private static IEnumerable<TResult> SelectMultiImpl<TSource, TResult>(
    IEnumerable<TSource> source, Func<TSource, TResult>[] selectors)
{
    foreach (TSource item in source)
    {
        foreach (var selector in selectors)
        {
            yield return selector(item);
        }
    }
}

Testing SelectMulti

We can now try out the new extension method. The code below recreates the concatenation example but without the requirement to parse the source sequence twice. Two lambda expressions are passed to the Func delegate array parameter. One returns the Person1 string and the other extracts the Person2 property. Note that the results are equivalent to the concatenation example but are generated in a different order due to the alternative approach.

IEnumerable<string> people = couples.SelectMulti(
    c => c.Person1,
    c => c.Person2
);

/* RESULTS

Bob
Sue
Mel
Jim
Tim
Jan

*/

Update: SelectMulti vs SelectMany

Since writing this article I've received a couple of messages comparing SelectMulti with the SelectMany standard query operator. The comments have specifically compared the example above to the code below, which creates a new array for each item in the source sequence, then flattens the arrays using SelectMany:

couples.SelectMany(c => new[] {c.Person1, c.Person2});

If you execute the above statement and fully enumerate the resultant sequence, the results are identical to the SelectMulti example. However, there are some subtle differences. One is that the SelectMany approach can process more items from the source sequence than strictly necessary; if you only wanted the first three people from the list in the example, the SelectMany version would also evaluate the fourth item. In the trivial example this would be a minor issue. However, if reading from a slow data source, or one with side-effects, this could cause a problem.

Another difference with the SelectMulti method is the way in which the projection functions are provided. As they are in a parameter array, you can store your functions in an array and provide them in one unit or as a series of comma-separated delegates. The encapsulation of the validation also means that it is possible to detect invalid functions immediately, whilst retaining the deferred execution.

2 September 2012