I have an array consisting of following elements:
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" }
};
The real object that i try to flatten is from importing data into array of arrays from CSV and then joining it on values of fields:
    var q =
        from c in list
        join p in vocatives on c.Line[name1].ToUpper() equals p.first_name.ToUpper() into ps
        from p in ps.DefaultIfEmpty()
        select new object[] { c.Line,  p == null ? "(No vocative)" : p.vocative, p == null ? "(No sex)" : p.sex }; 
I want to flatten that array of strings to get:
string[] {
    new string[]{ "1","2","3","4" },
    new string[]{ "5","6","7","8" },
    new string[]{ "9","10","11","12","13" }
}
I already have an solution that does that in loop, its not so performance-wise, but it seems to work ok.
I've tried to use SelectMany but cannot make up a solution.
Thank you very much for feedback ;) I've tried answer from npo:
var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray 
           ? (object[])y : new object[] { y })
);
But CSVwriter class method accepts only explicitly typed:
IEnumerable<string[]>
So how to do it in linq, I've tried to:
List<string[]> listOflists = (List<string[]>)result;
But no go, InvalidCastException arrises, unfortunately.
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:

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With