How do I flatten an array of arrays?

In a first step, you have to normalize the data to one kind of type. Then you can iterate over them as you like. So at first create a method to flatten the values from a specific point to an arbitrary depth:

public static class Extensions
{
    public static IEnumerable<object> FlattenArrays(this IEnumerable source)
    {
        foreach (var item in source)
        {
            if (item is IEnumerable inner
                && !(item is string))
            {
                foreach (var innerItem in inner.FlattenArrays())
                {
                    yield return innerItem;
                }
            }

            yield return item;
        }
    }
}

Now you can either iterate on the top level to get a single array of all values:

// Produces one array => ["1", "2", "3", "4", ...]
var allFlat = schools.FlattenArrays().OfType<string>().ToArray();

Or you can create individual array one depth deeper:

foreach (var item in schools)
{
    // Produces an array for each top level e.g. ["5", "6", "7", "8"]
    var flat = item.FlattenArrays().OfType<string>().ToArray();
}

As per the comments, because your inner array mixes elements of string[] and string, it likely won't be trivial to do this directly in Linq.

However, with the assistance of a helper function (I've called Flattener) you can branch the handling of both of the inner types manually to either return the elements in the array (if it's string[]), or to return the single element as an enumerable, if it's not. SelectMany can then be used to flatten the inner level, but the outer level seemingly you want to leave unflattened:

i.e.

var schools = new [] {
    new object[]{new[]{"1","2"}, "3","4"}, 
    new object[]{new[]{"5","6"}, "7","8"},
    new object[]{new[]{"9","10","11"}, "12","13"}
};

var result = schools
    .Select(s => s.SelectMany(o => Flattener(o)));

Which returns a type of IEnumerable<IEnumerable<string>>

Where the messy unpacking bit done by:

public IEnumerable<string> Flattener(object o)
{
    if (o is IEnumerable<string> strings)
    {
        return strings;
    }
    if (o is string s)
    {
       return new[]{s};
    }
    return new[]{"?"};
}

Note the above uses the pattern matching capabilities of C#7.

Result screenshot courtesy of LinqPad:

enter image description here

Tags:

C#

Linq

Arrays