views:

328

answers:

3

Just a little niggle about LINQ syntax. I'm flattening an IEnumerable<IEnumerable<T>> with SelectMany(x => x).

My problem is with the lambda expression x => x. It looks a bit ugly. Is there some static 'identity function' object that I can use instead of x => x? Something like SelectMany(IdentityFunction)?

+9  A: 

No, there isn't. It would have to be generic, to start with:

public static Func<T, T> IdentityFunction<T>()
{
    return x => x;
}

But then type inference wouldn't work, so you'd have to do:

SelectMany(Helpers.IdentityFunction<Foo>())

which is a lot uglier than x => x.

Another possibility is that you wrap this in an extension method:

public static IEnumerable<T> Flatten<T>
    (this IEnumerable<IEnumerable<T>> source)
{
    return source.SelectMany(x => x);
}

Unfortunately with generic variance the way it is, that may well fall foul of various cases in C# 3... it wouldn't be applicable to List<List<string>> for example. You could make it more generic:

public static IEnumerable<TElement> Flatten<TElement, TWrapper>
    (this IEnumerable<TWrapper> source) where TWrapper : IEnumerable<TElement>
{
    return source.SelectMany(x => x);
}

But again, you've then got type inference problems, I suspect...

EDIT: To respond to the comments... yes, C# 4 makes this easier. Or rather, it makes the first Flatten method more useful than it is in C# 3. Here's an example which works in C# 4, but doesn't work in C# 3 because the compiler can't convert from List<List<string>> to IEnumerable<IEnumerable<string>>:

using System;
using System.Collections.Generic;
using System.Linq;

public static class Extensions
{
    public static IEnumerable<T> Flatten<T>
        (this IEnumerable<IEnumerable<T>> source)
    {
        return source.SelectMany(x => x);
    }
}

class Test
{
    static void Main()
    {
        List<List<string>> strings = new List<List<string>>
        {
            new List<string> { "x", "y", "z" },
            new List<string> { "0", "1", "2" }
        };

        foreach (string x in strings.Flatten())
        {
            Console.WriteLine(x);
        }
    }
}
Jon Skeet
Thanks! That's given me some interesting leads too. I think I'll stay with x => x for the time being...
Joe
I tried to create a Flatten extension method too (which I called SelectMany but the goal was the same) and because of the problem with variance it wasn't really useful, I would need many overloads, so I went back to x => x...Jon, would a Flatten extension method be easier to do in C#4 / VB10?
Meta-Knight
@MetaKnight: Will edit post...
Jon Skeet
A: 

You can get close to what you need. Instead of a regular static function, consider an Extension Method for your IEnumerable<T>, as if the identity function is of the collection, not the type (a collection can generate the identity function of its items):

public static Func<T, T> IdentityFunction<T>(this IEnumerable<T> enumerable)
{
     return x => x;
}

with this, you don't have to specify the type again, and write:

IEnumerable<IEnumerable<T>> deepList = ... ;
var flat = deepList.SelectMany(deepList.IdentityFunction());

This does feel a bit abusive though, and I'd probably go with x=>x. Also, you cannot use it fluently (in chaining), so it will not always be useful.

Kobi
You need to return `Func<T, IEnumerable<T>>` to make it work with `SelectMany`.
Bryan Watts
True, it works for `Select` but fails for `SelectMany` - it cant figure out the types. shame.
Kobi
+1  A: 

Does this work in the way you want? I realize Jon posted a version of this solution, but he has a second type parameter which is only necessary if the resulting sequence type is different from the source sequence type.

public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source)
    where T : IEnumerable<T>
{
    return source.SelectMany(item => item);
}
Bryan Watts