SelectMany is a fascinating operator in LINQ to Objects. For one thing, it is not as intuitive as most other LINQ operators. MSDN says that SelectMany “projects each element of a sequence to an IEnumerable(T) and flattens the resulting sequences into one sequence.” I still remember reading this description of SelectMany for the first time, and wondering why that would that be useful.

Of course, SelectMany is not only incredibly useful, but also surprisingly powerful. In fact, a variety of LINQ operators are really just constrained versions SelectMany. Select, Concat, Where, Take, Skip, TakeWhile, SkipWhile and Distinct can all be easily rewritten using a single SelectMany.

To get started, let’s compare SelectMany with Select. SelectMany projects each element into some number of elements. In comparison, Select projects each element into exactly one element. Since SelectMany is more general than Select, it can be used to implement Select:

public static IEnumerable<U> Select<T, U>(
    this IEnumerable<T> source,
    Func<T, U> func)
{
    return source.SelectMany(x => Enumerable.Repeat(func(x), 1));
}

But, Select is not the only operator less general SelectMany. Similarly, you can think of the Where operator as producing zero or one element for each element in the sequence. So, Where can also be easily implemented using SelectMany:

public static IEnumerable<T> Where<T>(
    this IEnumerable<T> source,
    Func<T, bool> filter)
{
    return source.SelectMany(x => Enumerable.Repeat(x, filter(x) ? 1 : 0));
}

Another operator that can be easily implemented with SelectMany is Concat:

public static IEnumerable<T> Concat<T>(
    this IEnumerable<T> source1,
    IEnumerable<T> source2)
{
    return new int[] { 0, 1 }
        .SelectMany(
            x => x == 0 ? source1 : source2);
}

Take and Skip can be implemented using the SelectMany variant that passes indices into the user delegate:

public static IEnumerable<T> Take<T>(
    this IEnumerable<T> source,
    int toTake)
{
    return source.SelectMany((x, i) => Enumerable.Repeat(x, i < toTake ? 1 : 0));
}

public static IEnumerable<T> Skip<T>(
    this IEnumerable<T> source,
    int toSkip)
{
    return source.SelectMany((x, i) => Enumerable.Repeat(x, i >= toSkip ? 1 : 0));
}

And finally, with a few closure tricks, we can even implement TakeWhile, SkipWhile and Distinct:

public static IEnumerable<T> TakeWhile<T>(
    this IEnumerable<T> source,
    Func<T, bool> func)
{
    bool stopped = false;
    return source.SelectMany((x, i) => Enumerable.Repeat(x, (!stopped && (stopped = !func(x))) ? 1 : 0));
}

public static IEnumerable<T> SkipWhile<T>(
    this IEnumerable<T> source,
    Func<T, bool> func)
{
    bool started = false;
    return source.SelectMany((x, i) => Enumerable.Repeat(x, (started || (started = func(x))) ? 1 : 0));
}

public static IEnumerable<T> Distinct<T>(
    this IEnumerable<T> source)
{
    var dict = new Dictionary<T, int>();
    return source.SelectMany((x) => {
        if (!dict.ContainsKey(x))
        {
            dict.Add(x, 0);
            return new T[] { x };
        }
        return new T[] { };
    });
}

If you enjoyed this article, check out these LINQ puzzles. Also, read my article on the 7 tricks to simplify your programs with LINQ.

Tags:

8 Comments to “One LINQ operator to rule them all”

  1. Tom Pester says:

    Thank you for the insightful post. I am reading your linq posts with much interest.

  2. Thanks, Tom! It’s always great to get feedback on my blog posts, and even better if the feedback is positive. :-)

  3. Gürkan Alkan says:

    I always use “Select” in my exercises. But I understand that “SelectMany” is more useful. Thank you!!..

  4. Matt Warren says:

    Hi Tom,

    Thanks for the useful LINQ info, I appreciate it. Also your article on skip lists is facisnating.

    Cheers

  5. Дмитрий says:

    А Aggregate еще более общая, чем SelectMany, ага))
    Но в принципе, тогда уже можно и вручную все делать, без ФП. Хотя временами бывает полезно.

  6. Translation of comment from Дмитрий (from Google Translate):

    Aggregate And even more general than SelectMany, yeah))

    But in principle, only then can manually do everything, without AF. Although at times it may be useful.

  7. And yeah, Aggregate is equally powerful to SelectMany, but it does not give you deferred execution.

  8. Thomas James says:

    You did a great job! This post sound really nice.

    I saw something like a couple of weeks ago, something
    similar to this https://moonrisebaywine.com, but you did detailed study, and your post seems to be more persuasive than others.
    I am amazed by the arguments you provided as well as the manner of your post.
    I like when posts are both informative and interesting, when even boring facts are presented in an interactive way.
    Well, it is definitely about your post.

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>