Search code examples
c#arrayslinqreadonlyreadonly-collection

How to flatten out the concatenation of IEnumerables of IEnumerables


I know there's a LINQ solution somewhere, I just can't see it. The situation I have is this: I have a base class (lets call it B, for now) that accepts a vararg and uses it to initialize a readonly field, which happens to be a IReadonlyCollection (lets call this field List).

I have two derived classes (lets call them D1 and D2) that do the exact same thing with their vararg argument, passing it to the base class constructor, no problems there.

But I have a third derived class (lets call this one D_1_and_2), derived from the same base class, B, that accepts as parameters for its constructor 2 arrays, one of type D1[] and the other of type D2[].

My problem is, somehow I have to concatenate all elements of the first parameter's (the D1[] parameter) List fields with each other and with all the elements of the second paramenter's List fields and pass the resulting array to the base class during construction. Bellow is an illustration of the problem in code.

public class CollectionElement
{

}

public class BaseClass
{
    public readonly IReadOnlyCollection<CollectionElement> TheCollection;

    public BaseClass(params CollectionElement[] arg)
    {
        TheCollection = Arrays.AsReadOnly(arg);
    }
}

public class DerivedClassA : BaseClass
{
    public DerivedClass(params CollectionElement[] arg)
        :base(arg){}
}

public class DerivedClassB : BaseClass
{
    public DerivedClass(params CollectionElement[] arg)
        :base(arg){}
}

public class DerivedClassA_Plus_B : BaseClass
{
    public DerivedClass(DerivedClassA[] argA, DerivedClassB[] argB)
        :base(/*[don't know]*/){}
}

Solution

  • I think you're looking for Enumerable.SelectMany to project and then flatten the elements, and then Enumerable.Concat to join the two sequences together.

    public class DerivedClassA_Plus_B : BaseClass
    {
        public DerivedClass(DerivedClassA[] argA, DerivedClassB[] argB)
            :base(Combine(argA, argB)) { }
    
        private static CollectionElement[] Combine(DerivedClassA[] argA, DerivedClassB[] argB)
        {
            var a = argA.SelectMany(x => x.TheCollection);
            var b = argB.SelectMany(x => x.TheCollection);
            return a.Concat(b).ToArray();
        }
    }