Elegant way to combine multiple collections of elements?
Say I have an arbitrary number of collections, each containing objects of the same type (for example, List<int> foo
and List<int> bar
). If these collections were themselves in a collection (e.g., of type List<List<int>>
, I could use SelectMany
to combine them all into one collection.
However, if these collections are not already in the same collection, it's my impression that I'd have to write a method like this:
public static IEnumerable<T> Combine<T>(params ICollection<T>[] toCombine)
{
return toCombine.SelectMany(x => x);
}
Which I'd then call like this:
var combined = Combine(foo, bar);
Is there a clean, elegant way to combine (any number of) collections without having to write a utility method like Combine
above? It seems simple enough that there should be a way to do it in LINQ, but perhaps not.
I think you might be looking for LINQ's .Concat()
?
var combined = foo.Concat(bar).Concat(foobar).Concat(...);
Alternatively, .Union()
will remove duplicate elements.
To me Concat
as an extension method is not very elegant in my code when I have multiple large sequences to concat. This is merely a codde indentation/formatting problem and something very personal.
Sure it looks good like this:
var list = list1.Concat(list2).Concat(list3);
Not so readable when it reads like:
var list = list1.Select(x = > x)
.Concat(list2.Where(x => true)
.Concat(list3.OrderBy(x => x));
Or when it looks like:
return Normalize(list1, a, b)
.Concat(Normalize(list2, b, c))
.Concat(Normalize(list3, c, d));
or whatever your preferred formatting is. Things get worse with more complex concats. The reason for my sort of cognitive dissonance with the above style is that the first sequence lie outside of the Concat
method whereas the subsequent sequences lie inside. I rather prefer to call the static Concat
method directly and not the extension style:
var list = Enumerable.Concat(list1.Select(x => x),
list2.Where(x => true));
For more number of concats of sequences I carry the same static method as in OP:
public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences)
{
return sequences.SelectMany(x => x);
}
So I can write:
return EnumerableEx.Concat
(
list1.Select(x = > x),
list2.Where(x => true),
list3.OrderBy(x => x)
);
Looks better. The extra, otherwise redundant, class name I have to write is not a problem for me considering my sequences look cleaner with the Concat
call. It's less of a problem in C# 6. You can just write:
return Concat(list1.Select(x = > x),
list2.Where(x => true),
list3.OrderBy(x => x));
Wished we had list concatenation operators in C#, something like:
list1 @ list2 // F#
list1 ++ list2 // Scala
Much cleaner that way.
For the case when you do have a collection of collections, i.e. a List<List<T>>
, Enumerable.Aggregate
is a more elegant way to combine all lists into one:
var combined = lists.Aggregate((acc, list) => { return acc.Concat(list); });