15

Erik Meijer is fond of pointing out that every LINQ function could actually be implemented by SelectMany; everything else is just a convenience.

This is what Eric Lippert says answering a question about monads, but I've heard Erik Meijer say this in other videos about LINQ and Rx. (Simply put, Erik Meijer is the guy who created LINQ and Rx)

I wonder how you would implement some of the most used LINQ functions with SelectMany? Ignore perfromance for now, let's focus on elegance and succinctness.

  • Where
  • Select
  • First
  • Take(n)
  • TakeWhile
  • GroupBy
  • OrderBy
  • Zip
  • Others...
Community
  • 1
  • 1
lukebuehler
  • 4,061
  • 2
  • 24
  • 28
  • 6
    Reimplementing operators using `SelectMany` :http://msmvps.com/blogs/jon_skeet/archive/2010/12/27/reimplementing-linq-to-objects-part-9-selectmany.aspx – Tim Schmelter Oct 23 '13 at 13:22
  • Great link, but he only implements Select, Where and Concat, those seem to be the easiest. – lukebuehler Oct 23 '13 at 13:26
  • 5
    I don't know why this is on hold. I understand this question deeply, it's important to understand how the "bind" operator or select many in C# can be used to create other monadic functions. And I think many developers, including me, can learn from the answers to this question. Now, I don't to go into those functional concepts in this question because I think this question is interesting in and of itself. – lukebuehler Oct 23 '13 at 14:07
  • It's out of the scope of what you can ask on stackoverflow currently. http://stackoverflow.com/help/on-topic So it's an intersting question, but it's not a specific programming problem. – Tim Schmelter Oct 23 '13 at 14:49

2 Answers2

2

The main bit to keep in mind is that SelectMany works on an IEnumerable and returns an IEnumerable using lambda expressions that have access to the current item and its index. So anything you could do to transform the result with access to the current item or its index is possible:

  • Decide which elements to keep or throw away (Where, First, Take, Skip, TakeWhile)
  • Change elements and return new ones (Select)
  • Do other stuff? (GroupBy, honestly don't know how I would implement that without some thought)

This simple Where example will make it clear how many of these could be accomplished easily:

SomeList.SelectMany(x =>
    ShouldBeIncluded(x) ?
        Enumerable.Repeat(x, 1) :
        Enumerable.Empty<AClass>();
    );

Edit - Great link posted by Tim Schmelter in the comments proves again that Jon Skeet has already done it cleaner:

return Enumerable.Repeat(x, ShouldBeIncluded(x) ? 1 : 0;
Ocelot20
  • 10,510
  • 11
  • 55
  • 96
  • In case anyone is looking for that link, it seems to have moved to here... https://codeblog.jonskeet.uk/2010/12/27/reimplementing-linq-to-objects-part-9-selectmany/ – Avrohom Yisroel Jan 01 '20 at 17:35
0

Well, this will return an Enumerable<T> with zero or one element, better than nothing :

public static IEnumerable<T> FirstOrDefault<T>(this IEnumerable<T> source)
{
    return source.SelectMany((t, index) => Enumerable.Repeat(t, index == 0 ? 1 : 0));
}

Don't see how to return only a T with SelectMany.

Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122