Elegant way to combine multiple collections of elements?
Solution 1
I think you might be looking for LINQ's .Concat()
?
var combined = foo.Concat(bar).Concat(foobar).Concat(...);
Alternatively, .Union()
will remove duplicate elements.
Solution 2
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.
Solution 3
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); });
Solution 4
Use Enumerable.Concat
like so:
var combined = foo.Concat(bar).Concat(baz)....;
Solution 5
You could always use Aggregate combined with Concat...
var listOfLists = new List<List<int>>
{
new List<int> {1, 2, 3, 4},
new List<int> {5, 6, 7, 8},
new List<int> {9, 10}
};
IEnumerable<int> combined = new List<int>();
combined = listOfLists.Aggregate(combined, (current, list) => current.Concat(list)).ToList();
Comments
-
Donut almost 3 years
Say I have an arbitrary number of collections, each containing objects of the same type (for example,
List<int> foo
andList<int> bar
). If these collections were themselves in a collection (e.g., of typeList<List<int>>
, I could useSelectMany
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. -
jason over 13 yearsNo!
Enumerable.Union
is a set union which does not produce the desired result (it only yields duplicates once). -
Donut over 13 yearsThanks; the only reason I didn't do this in the first place is that (to me) it seems to get uglier the more collections you have to deal with. However this has the benefit of using existing LINQ functions, which future developers will likely already be familiar with.
-
Donut over 13 yearsYeah, I need the specific instances of the objects, as I need to do some special processing on them. Using
int
in my example might have thrown people off a little bit; butUnion
won't work for me in this case. -
Donut over 13 yearsI like the extension method idea, I just didn't want to reinvent the wheel if LINQ already had a method that would do the same thing (and be immediately to understandable to other developers further down the line)
-
Bryan Rayner about 8 yearsI appreciate your concern for coding style. This is much more elegant.
-
Bryan Rayner about 8 yearsPerhaps a smaller version of your answer can be merged with the top answer here.
-
brichins over 7 yearsI like this formatting as well - although I prefer to perform the individual list operations (e.g.
Where
,OrderBy
) first for clarity, especially if they're anything more complex than this example. -
Clay about 6 yearsThe best solution so far.
-
nawfal about 5 yearsHow do you get the
lists
here first? That's the first problem OP has. If he had acollection<collection>
to start with thenSelectMany
is just way simpler. -
nawfal about 5 years@Oleg
SelectMany
is just simpler. -
astreltsov about 5 yearsNot sure what happened, but this does seem to be answering the wrong question. Edited answer to reflect this. Many people do seem to end up here because the need to combine a List<List<T>>
-
Gonzo345 about 5 yearsThanks for the
.Union()
tip. Bear in mind you will need to implement IComparer on your custom type in case it is. -
Jay over 2 yearsThe documentation Says you have to implement
IEquatable<T>
while in fact you only need to overrideEquals
andGetHashCode