Angenommen, ich habe eine beliebige Anzahl von Sammlungen, die jeweils Objekte desselben Typs enthalten (beispielsweise List<int> foo
und List<int> bar
). Wenn diese Sammlungen selbst in einer Sammlung wären (z. B. vom Typ List<List<int>>
), könnte ich SelectMany
verwenden, um alle in einer Sammlung zusammenzufassen.
Wenn sich diese Sammlungen jedoch nicht bereits in derselben Sammlung befinden, habe ich den Eindruck, dass ich eine Methode wie diese schreiben müsste:
public static IEnumerable<T> Combine<T>(params ICollection<T>[] toCombine)
{
return toCombine.SelectMany(x => x);
}
Was ich dann so nennen würde:
var combined = Combine(foo, bar);
Gibt es eine saubere, elegante Möglichkeit, (beliebig viele) Sammlungen zu kombinieren, ohne oben eine Utility-Methode wie Combine
schreiben zu müssen? Es scheint einfach genug, dass es in LINQ eine Möglichkeit gibt, dies zu tun, aber vielleicht nicht.
Ich glaube, Sie suchen nach LINQs .Concat()
?
var combined = foo.Concat(bar).Concat(foobar).Concat(...);
Alternativ entfernt .Union()
doppelte Elemente.
Für mich ist Concat
als Erweiterungsmethode in meinem Code nicht sehr elegant, wenn ich mehrere große Sequenzen zu concat habe. Dies ist lediglich ein Codde-Einrückungs-/Formatierungsproblem und etwas sehr Persönliches.
Sicher sieht es so aus:
var list = list1.Concat(list2).Concat(list3);
Nicht so lesbar, wenn es wie folgt lautet:
var list = list1.Select(x = > x)
.Concat(list2.Where(x => true)
.Concat(list3.OrderBy(x => x));
Oder wenn es so aussieht:
return Normalize(list1, a, b)
.Concat(Normalize(list2, b, c))
.Concat(Normalize(list3, c, d));
oder was auch immer Ihre bevorzugte Formatierung ist. Bei komplexeren Concats wird es schlimmer. Der Grund für meine Art von kognitiver Dissonanz mit dem obigen Stil liegt darin, dass die erste Sequenz außerhalb der Concat
-Methode liegt, während die nachfolgenden Sequenzen im Inneren liegen. Ich ziehe es vor, die statische Concat
-Methode direkt und nicht den Erweiterungsstil aufzurufen:
var list = Enumerable.Concat(list1.Select(x => x),
list2.Where(x => true));
Für mehr Concats von Sequenzen trage ich dieselbe statische Methode wie in OP:
public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences)
{
return sequences.SelectMany(x => x);
}
So kann ich schreiben:
return EnumerableEx.Concat
(
list1.Select(x = > x),
list2.Where(x => true),
list3.OrderBy(x => x)
);
Sieht besser aus. Der zusätzliche, ansonsten redundante Klassenname, den ich schreiben muss, ist für mich kein Problem, da meine Sequenzen mit dem Aufruf Concat
sauberer aussehen. In C # 6 ist das Problem weniger. Sie können einfach schreiben:
return Concat(list1.Select(x = > x),
list2.Where(x => true),
list3.OrderBy(x => x));
Ich wünschte, wir hätten Verkettungsoperatoren in C #, etwa so:
list1 @ list2 // F#
list1 ++ list2 // Scala
Viel sauberer auf diese Weise.
Enumerable.Aggregate
ist der richtige "elegante" Weg, dies zu tun:
var all = lists.Aggregate((acc, list) => { return acc.Concat(list); });
Verwenden Sie Enumerable.Concat
wie folgt:
var combined = foo.Concat(bar).Concat(baz)....;
Verwenden Sie Enumerable.Concact
:
var query = foo.Concat(bar);
Die einzige Möglichkeit, die ich sehe, ist die Verwendung von Concat()
.
var foo = new List<int> { 1, 2, 3 };
var bar = new List<int> { 4, 5, 6 };
var tor = new List<int> { 7, 8, 9 };
var result = foo.Concat(bar).Concat(tor);
Aber du solltest entscheiden, was besser ist:
var result = Combine(foo, bar, tor);
oder
var result = foo.Concat(bar).Concat(tor);
Ein Grund , warum Concat()
die bessere Wahl ist ist, dass es für einen anderen Entwickler offensichtlicher ist. Lesbarer und einfacher.
Sie können Aggregate immer mit Concat kombinieren ...
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();
Sie können Union wie folgt verwenden:
var combined=foo.Union(bar).Union(baz)...
Dadurch werden identische Elemente entfernt. Wenn Sie also solche Elemente haben, können Sie stattdessen Concat
verwenden.
Da Sie mit einer Reihe separater Kollektionen beginnen, finde ich, dass Ihre Lösung ziemlich elegant ist. Sie müssen etwas tun, um sie zusammenzunähen.
Synthetischer wäre es sinnvoller, aus Ihrer Combine-Methode eine Erweiterungsmethode zu erstellen, die sie überall verfügbar macht.
Alles was Sie brauchen ist das für jeden IEnumerable<IEnumerable<T>> lists
:
var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));
Dadurch werden alle Elemente in lists
zu einem IEnumerable<T>
(mit Duplikaten) zusammengefasst. Verwenden Sie Union
anstelle von Concat
, um Duplikate zu entfernen, wie in den anderen Antworten angegeben.