0

I have the following property:

public List<List<MyClass>> Items { get; set;}

This is bound to a ListViews ItemSource which is IEnumerable.

It is this IEnumerable ItemSource property that I am now trying to flatten.

I have managed to cast it to the following

this.ItemsSource.Cast<IList>().ToList();

because the following cast threw an "Invalid Cast Exception":

this.ItemsSource.Cast<List<object>>().ToList();

I am now looking to flatten this list to just a straight list of objects. I looked at this answer: Flatten List in LINQ and did:

this.ItemsSource.Cast<IList>().ToList().SelectMany(x => x);

But I get this error:

'List' does not contain a definition for 'SelectMany' and no extension method 'SelectMany' accepting a first argument of type 'List' could be found (are you missing a using directive or an assembly reference?)

So what am I doing wrong? Is it possible to flatten a List<IList>()?

Extra Information:

It may be worth mentioning that I am using Xamarin and this code is part of my PCL (portable class library) although I'm sure this wont be the reason.

While investigating what's going on here I have also tried:

List<string> s = new List<string>();
s.SelectMany(x => x);

and I get the error:

The type arguments for method 'Enumerable.SelectMany<TSource, TResult>(IEnumerable, Func<TSource, IEnumerable>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
JKennedy
  • 18,150
  • 17
  • 114
  • 198

4 Answers4

3

Cast it to the right type first, then you can use SelectMany:

var source = (List<List<MyClass>>) this.ItemsSource;
IEnumerable<MyClass> items = source.SelectMany(list => list);
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
3

Try this:

this.ItemsSource.Cast<IEnumerable>().SelectMany(sublist => sublist.Cast<object>()).ToList()

I tested this with this sample program, and it compiled, and run:

class Test
{
    public List<List<int>> Items { get; set; }

    public IEnumerable ItemsSource { get { return this.Items; } }

    public List<object> Flatten
    {
        get { return this.ItemsSource.Cast<IEnumerable>().SelectMany(sublist => sublist.Cast<object>()).ToList(); }
    }
}

static class Program
{
    static void Main()
    {
        var t = new Test();
        t.Items = new List<List<int>>
        {
            new List<int> { 1, 2 },
            new List<int> { 11, 12 }
        };

        var r = t.Flatten;
    }
}

I assumed that, even though you know the type of the items, you pretend not to know when using the flattened list... that is why it returns a list of objects instead of a list of ints.

Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82
  • This works. Thanks! The ItemsSource is a Lists ItemsSource hence why I dont cast to specific type. I'm glad someone believed me that it wasn't as straight forward as "Add the using Statement" thanks. Still shocked how you managed to come to this conclusion. I dont think I would have EVER solved this – JKennedy Feb 17 '16 at 14:47
  • I believed because I really opened Visual Studio, created a class, along with a test program... and then I could see the issue. Patience and methodology always works! – Miguel Angelo Feb 17 '16 at 14:53
  • 1
    Don't think that of yourself ("not being able to EVER solve this")... next time someone asks you will know. That's how experience works. =D – Miguel Angelo Feb 17 '16 at 14:57
0

Just do -

var flattened = Items.SelectMany(a => a);
Amit Kumar Ghosh
  • 3,618
  • 1
  • 20
  • 24
  • Tried `ItemsSource.SelectMany(x => x)` and get the following error: IEnumerable does not contain a definition for SelectMany.... – JKennedy Feb 17 '16 at 14:25
  • @user1, the answer is correct, you need to import `System.Linq` – Michael Mairegger Feb 17 '16 at 14:31
  • This answer is incorrect because I am using the `ItemsSource` property which is `IEnumerable` not the `Items` class which was a specific type. Although this code will compile and work it isnt the answer to the question – JKennedy Feb 17 '16 at 14:49
0

Ok so interresting point here. This is an extension of @TimSchmelters answer explaining why casting to the right type matters

From my second error in the aside which was:

The type arguments for method 'Enumerable.SelectMany(IEnumerable, Func>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

after some googling I found this answer to the question: "SelectMany() Cannot Infer Type Argument — Why Not?"

This states this error occurs because of the following:

The type returned by the delegate you pass to SelectMany must be an IEnumerable

So the error boils down to how I am casting it:

I must cast it to IEnumerable<T> and obviously IList isnt that. If I do the following however it now works:

this.ItemsSource.Cast<IEnumerable<object>>().ToList().SelectMany(x => x);
Community
  • 1
  • 1
JKennedy
  • 18,150
  • 17
  • 114
  • 198