it-swarm.com.de

Elegante Möglichkeit, mehrere Elementesammlungen zu kombinieren?

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.

67
Donut

Ich glaube, Sie suchen nach LINQs .Concat() ?

var combined = foo.Concat(bar).Concat(foobar).Concat(...);

Alternativ entfernt .Union() doppelte Elemente.

78
Domenic

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.

20
nawfal

Enumerable.Aggregate ist der richtige "elegante" Weg, dies zu tun:

var all = lists.Aggregate((acc, list) => { return acc.Concat(list); });
17
astreltsov

Verwenden Sie Enumerable.Concat wie folgt:

var combined = foo.Concat(bar).Concat(baz)....;
14
jason

Verwenden Sie Enumerable.Concact :

var query = foo.Concat(bar);
7
Ahmad Mageed

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.

6
Restuta

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();
6
drs

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.

2

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.

1
Dave Swersky

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.

0
Nir Maoz