-3

If you have an IEnumerable<IGrouping<K, T>>, say from

var ans = src.GroupBy(s => s.Field);

Is there a better way (rarely used LINQ method/variation) to convert to an IEnumerable<IEnumerable<T>> then nested Selects like so:

var ans2 = ans.Select(g => g.Select(s => s));

For those that aren't understanding,

var anst = ans2.Select(g => g.GetType().ReflectedType).All(t => t == typeof(Enumerable));

should return anst == true.

NetMage
  • 26,163
  • 3
  • 34
  • 55
  • 2
    What interface is `IGrouping`? I only know of `IGrouping`, which is already `IEnumerable`. And since `IEnumerable` is covariant, an `IEnumerable>` is already automatically an `IEnumerable>` What exactly are you asking here? – Eric Lippert Mar 16 '18 at 00:34
  • 1
    Your test doesn't match your question... In `IEnumerable ans2 = Enumerable.Repeat(new List(),1);` ans2 is of type `IEnumerable` but your test would return `anst` = false. This is because the only type for which `t == typeof(Enumerable)` would return true is `Enumerable` (not the interface, the actual class Enumerable). This is almost certainly *not* what you are intending. – Chris Mar 16 '18 at 00:39
  • Is this perhaps an A/B problem? What are you trying to do that you feel you need to do this conversion? As others have said you already have something that implements `IEnumerable`. Are you actually having a different problem for which you think the above is the solution? – Chris Mar 16 '18 at 00:41
  • 1
    You have a very strange requirement, so I will not put this in my answer, but looking at the reference source, if you can modify your call to GroupBy this will pass your test: src.GroupBy(s => s.Field), (key, items) => items.Skip(0)); – Jason Mar 16 '18 at 00:48
  • If you meant to convert GroupEnumerable to Enumerable, you already have the slickest way possible. No need to look anywhere else. Unless you have a problem with that nested select. – Erlangga Hasto Handoko Mar 16 '18 at 03:31
  • @Jason `Skip(0)` is a very neat way to accomplish the same thing. Nice. – NetMage Mar 16 '18 at 15:34
  • @Chris A bit late ;) but your `ans2` is not of type `IEnumerable>` but rather `IEnumerable>` which would show up when doing something like `Skip()` on the groups which would optimize for `List` since that is the actual type. Note that the use of `ReflectedType` on the returned type causes `Enumerable` to be returned for most LINQ operator classes. – NetMage Jun 03 '21 at 18:43
  • @Jason If you modify you answer to use `Skip(0)` I will accept it, as that has come closest to simplifying - especially since there are some optimizations for `0` in the .Net Core `Skip` method. Since `Enumerable.Select` takes a `Func` there is no optimization possible for `Select(x => x)` though perhaps there should be. – NetMage Jun 03 '21 at 18:58
  • It was a long time ago but I think my point was that `IEnumerable> ans2 = Enumerable.Repeat(new List(),1);` is a perfectly valid line of code. So while the actual type is `IEnumerable>` it is actually also a `IEnumerable>` so your test for whether something is an `IEnumerable>` is disagreeing with the c' compiler on whether something matches that type. Your anst check is a much more strict check of the input and it made no sense to me as to why you *needed* the reflected Types to all be Enumerable. – Chris Jun 04 '21 at 09:55
  • ie what exactly was the problem that required your input to match that anst check and to reject something that could be assigned to `IEnumerable>`. It feels like perhaps the fix was not to hack your input around with things like `.Skip(0)` but to fix whatever the real problem was that left you wanting to do this. I would imagine that whatever that real problem was though is a long time in the past now though... – Chris Jun 04 '21 at 09:57
  • @Chris one purpose is to bypass LINQ optimizations in methods like `Take` and `Skip` for specific types such as `IList` or `T[]`. – NetMage Jun 05 '21 at 00:08

2 Answers2

1

One solution to avoid the nested select is using the AsEnumerable() method.

var ans = src.GroupBy(s => s.Field).Select(e => e.AsEnumerable());
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • Turns out `IGrouping.AsEnumerable()` returns `IGrouping`. Who knew? – NetMage Mar 15 '18 at 23:59
  • @NetMage yup, `IGrouping.AsEnumerable()` returns `IGrouping` but it does the job of having the result set be an `IEnumerable>` without a "nested select" as your question suggests. – Ousmane D. Mar 16 '18 at 00:04
  • But that `Select` is literally doing nothing. Leaving it off would get the same result because `IGrouping` *is* an `IEnumerable`. – NetMage Mar 16 '18 at 00:06
  • @NetMage Yes, `IGrouping` **is** an `IEnumerable` because it Implements `IEnumerable`. what exactly are you trying to accomplish then? if having an `IEnumerable>` as you've initially asked doesn't satisfy you? – Ousmane D. Mar 16 '18 at 00:14
  • I added a test in my answer. – NetMage Mar 16 '18 at 00:31
1

In this case IGrouping already implements that interface:

public static class Program
{
    static void Main(string[] args)
    {
        var foo = new[] {
            new MyClass { a = "1", b = "2"},
            new MyClass { a = "1", b = "3"},
            new MyClass { a = "2", b = "4"},
            new MyClass { a = "2", b = "5"} };

        var ans = foo.GroupBy(x => x.a);

        IEnumerable<IEnumerable<MyClass>> ans2 = ans;
        IEnumerable<IEnumerable<MyClass>> result = ans2.ToArray();
    }

    private class MyClass
    {
        public string a;
        public string b;
    }
}
Jason
  • 369
  • 1
  • 4
  • Why are you casting result to an array ? – Kevin Avignon Mar 15 '18 at 23:58
  • Turns out `IGrouping.Cast>()` returns `IGrouping`. – NetMage Mar 16 '18 at 00:00
  • ToArray will force an enumeration of the IEnumerable so I will see an exception thrown if something went wrong. Before that line, none of the grouping or casting code would actually do any work. – Jason Mar 16 '18 at 00:01
  • NetMage, Cast returns IEnumerable - the T objects may also happen to implement IGrouping, but that's not relevant here. Instead of var ans2 above, you can write IEnumerable> ans2 and it works fine. I edited my answer to remove the var to make this more clear. – Jason Mar 16 '18 at 00:04
  • You will see that if you remove the `Cast` from the assigment to `ans2`, the code still compiles and works. The `Cast` _does nothing_. – NetMage Mar 16 '18 at 00:31
  • 1
    @NetMage: Exactly. An `IEnumerable>` is *already* an `IEnumerable>`. The conversion you want is *built in to the language*, so it is very unclear what you are asking for. You've already got it. – Eric Lippert Mar 16 '18 at 00:37
  • I had added the Cast<> for clarity before when using 'var' for ans2 but the explicit types instead of var show it even more clearly - so removed Cast<>. – Jason Mar 16 '18 at 01:38
  • @EricLippert There may be no technical reason (make `Key` inaccessible?, make Reflection expecting `Enumerable` work? - whie minimizing overhead) - it mainly changes the way the LINQPad `Dump` method displays the results. – NetMage Mar 16 '18 at 15:35
  • @EricLippert I have since come up with another reason - to change the way LINQ extension methods optimize when applied to the groups. They use `GetType` to determine the runtime object and optimize on that. – NetMage Jun 03 '21 at 18:46