Führen Sie zwei (oder mehr) Listen in C # .NET zu einer zusammen

246

Ist es möglich, zwei oder mehr Listen in .NET mit C # in eine einzige Liste zu konvertieren?

Beispielsweise,

public static List<Product> GetAllProducts(int categoryId){ .... }
.
.
.
var productCollection1 = GetAllProducts(CategoryId1);
var productCollection2 = GetAllProducts(CategoryId2);
var productCollection3 = GetAllProducts(CategoryId3);
Zekia
quelle
Möchten Sie productCollection1 2 und 3 zusammenführen?
Meinen Sie damit, mehr als eine Liste in einer Liste zusammenzuführen?
Amr Badawy
Ihr Beispiel verwirrt mich ... suchen Sie die AddRangeMethode?
Bobby

Antworten:

457

Sie können den LINQ Concatund die ToListMethoden verwenden:

var allProducts = productCollection1.Concat(productCollection2)
                                    .Concat(productCollection3)
                                    .ToList();

Beachten Sie, dass es effizientere Möglichkeiten gibt, dies zu tun. Im Folgenden werden grundsätzlich alle Einträge durchlaufen, wodurch ein Puffer mit dynamischer Größe erstellt wird. Wie Sie die Größe , mit zu beginnen vorhersagen können, brauchen Sie nicht diese dynamische Dimensionierung ... so Sie könnten verwenden:

var allProducts = new List<Product>(productCollection1.Count +
                                    productCollection2.Count +
                                    productCollection3.Count);
allProducts.AddRange(productCollection1);
allProducts.AddRange(productCollection2);
allProducts.AddRange(productCollection3);

( AddRangeist speziell ICollection<T>für Effizienz ausgelegt.)

Ich würde diesen Ansatz nicht wählen, wenn Sie es nicht wirklich müssen.

Jon Skeet
quelle
4
BTW, productCollection1.ForEach(p => allProducts.Add(p))ist performanter als AddRange.
Marc Climent
8
@MarcCliment: Das ist eine ziemlich kühne pauschale Anweisung - insbesondere, wenn es AddRangedarum geht, eine Blockkopie von einem zugrunde liegenden Array in ein anderes zu erstellen . Haben Sie Links, um dies zu beweisen?
Jon Skeet
4
@MarcCliment: Zum einen erstellen Sie in Ihren Tests nicht die Liste mit der richtigen Endgröße - während der Code in meiner Antwort dies tut. Wenn Sie dies tun, ist der 10000 * 10000-Test mit AddRange zumindest schneller - obwohl andere Ergebnisse inkonsistent sind. (Sie sollten auch die Speicherbereinigung zwischen den Tests erzwingen - und ich würde argumentieren, dass die sehr kurzen Tests bedeutungslos klein sind.)
Jon Skeet
6
@MarcCliment: Welcher spezielle Fall? Welche CLR? Welche CPU-Architektur? Auf meiner Maschine erhalte ich eine Mischung aus Ergebnissen. Aus diesem Grund productionCollection1.ForEach(...)hasse ich es, pauschale Anweisungen wie "Übrigens, ist leistungsfähiger als AddRange" anzugeben / zu akzeptieren . Leistung ist sehr selten so einfach zu beschreiben. Ich bin überrascht, dass AddRange es nicht leicht schlägt - ich muss das weiter untersuchen.
Jon Skeet
15
Ich habe das Wesentliche ( gist.github.com/mcliment/4690433 ) mit dem Leistungstest unter Verwendung von BenchmarksDotNet aktualisiert und ordnungsgemäß getestet. DiesAddRange ist die beste Option für die Rohgeschwindigkeit ( ungefähr 4x und je größer die Listen, desto besser die Steigerung), wie Jon vorschlagen.
Marc Climent
41

Angenommen, Sie möchten eine Liste mit allen Produkten für die angegebenen Kategorie-IDs, können Sie Ihre Abfrage als Projektion behandeln, gefolgt von einem Reduzierungsvorgang . Es gibt einen LINQ-Operator, der das macht : SelectMany.

// implicitly List<Product>
var products = new[] { CategoryId1, CategoryId2, CategoryId3 }
                     .SelectMany(id => GetAllProducts(id))
                     .ToList();

In C # 4 können Sie SelectMany verkürzen auf: .SelectMany(GetAllProducts)

Wenn Sie bereits Listen haben, die die Produkte für jede ID darstellen, benötigen Sie eine Verkettung , wie andere betonen.

Ani
quelle
8
Wenn das OP die einzelnen Listen aus einem anderen Grund nicht benötigt, ist dies eine großartige Lösung.
Jon Skeet
2
Ich hatte ein ähnliches Problem und wollte gerade aufgeben und eine Frage stellen, als ich diese fand. Dies ist die beste Antwort. Besonders wenn der Code diese Kategorien bereits in einer Liste oder einem Array enthält, was in meinem Fall der Fall war.
22

Sie können sie mit LINQ kombinieren:

  list = list1.Concat(list2).Concat(list3).ToList();

Der traditionellere Ansatz der Verwendung List.AddRange()könnte jedoch effizienter sein.

Botz3000
quelle
11

Schauen Sie sich List.AddRange an, um Listen zusammenzuführen

StuartLC
quelle
8

Sie können die Concat- Erweiterungsmethode verwenden:

var result = productCollection1
    .Concat(productCollection2)
    .Concat(productCollection3)
    .ToList();
Darin Dimitrov
quelle
8
list4 = list1.Concat(list2).Concat(list3).ToList();
Decyklon
quelle
4

Ich weiß, dass dies eine alte Frage ist, von der ich dachte, ich könnte nur meine 2 Cent hinzufügen.

Wenn Sie eine haben List<Something>[], können Sie sie mit verbindenAggregate

public List<TType> Concat<TType>(params List<TType>[] lists)
{
    var result = lists.Aggregate(new List<TType>(), (x, y) => x.Concat(y).ToList());

    return result;
}

Hoffe das hilft.

sQuir3l
quelle
2

Ich habe es bereits kommentiert, aber ich denke immer noch, dass es eine gültige Option ist. Testen Sie einfach, ob in Ihrer Umgebung die eine oder andere Lösung besser ist. In meinem speziellen Fall ist die Verwendung source.ForEach(p => dest.Add(p))besser als der Klassiker, AddRangeaber ich habe nicht untersucht, warum dies auf niedrigem Niveau der Fall ist .

Einen Beispielcode finden Sie hier: https://gist.github.com/mcliment/4690433

Die Option wäre also:

var allProducts = new List<Product>(productCollection1.Count +
                                    productCollection2.Count +
                                    productCollection3.Count);

productCollection1.ForEach(p => allProducts.Add(p));
productCollection2.ForEach(p => allProducts.Add(p));
productCollection3.ForEach(p => allProducts.Add(p));

Testen Sie es, um zu sehen, ob es für Sie funktioniert.

Haftungsausschluss : Ich befürworte diese Lösung nicht, ich finde Concatdie klarste. Ich habe gerade in meiner Diskussion mit Jon festgestellt, dass dieser Fall in meiner Maschine eine bessere Leistung erbringt als AddRange, aber er sagt mit weitaus mehr Wissen als ich, dass dies keinen Sinn ergibt. Es gibt das Wesentliche, wenn Sie vergleichen möchten.

Marc Climent
quelle
Unabhängig von Ihrer Debatte mit Jon Skeet in den Kommentaren bevorzuge ich die Sauberkeit, die seine vorgeschlagene Lösung bietet. Dies fügt lediglich eine unnötige Interpretation der Absicht des Codes hinzu.
Vix
1
Ich denke, die klarste Option ist Concat als Verallgemeinerung von AddRange, semantisch korrekter und ändert die Liste nicht an Ort und Stelle, wodurch sie verkettbar wird. Bei der Diskussion mit Jon ging es um Leistung, nicht um Sauberkeit.
Marc Climent
Ich habe so etwas: Das var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();fügt es der GridView hinzu, aber als eine Spalte. Ich möchte sie in drei separate Spalten aufteilen.
Si8
2
// I would make it a little bit more simple

 var products = new List<List<product>> {item1, item2, item3 }.SelectMany(id => id).ToList();

Auf diese Weise handelt es sich um eine mehrdimensionale Liste, und .SelectMany () reduziert sie in eine IEnumerable von Produkten. Anschließend verwende ich die Methode .ToList ().

Gringo Jaimes
quelle
2

Zusammenführen oder Kombinieren zu Listen zu einer Ein-Liste.

  • Eines muss zutreffen: Der Typ beider Listen ist gleich.

  • Zum Beispiel : Wenn wir eine Liste von haben string, können wir der vorhandenen Liste eine weitere Liste hinzufügen, die eine Liste mit Typzeichenfolgen enthält, andernfalls können wir dies nicht.

Beispiel :

class Program
{
   static void Main(string[] args)
   {
      List<string> CustomerList_One = new List<string> 
      {
         "James",
         "Scott",
         "Mark",
         "John",
         "Sara",
         "Mary",
         "William",
         "Broad",
         "Ben",
         "Rich",
         "Hack",
         "Bob"
      };

      List<string> CustomerList_Two = new List<string> 
      {
         "Perter",
         "Parker",
         "Bond",
         "been",
         "Bilbo",
         "Cooper"
      };

      // Adding all contents of CustomerList_Two to CustomerList_One.
      CustomerList_One.AddRange(CustomerList_Two);

      // Creating another Listlist and assigning all Contents of CustomerList_One.
      List<string> AllCustomers = new List<string>();

      foreach (var item in CustomerList_One)
      {
         AllCustomers.Add(item);
      }

      // Removing CustomerList_One & CustomerList_Two.
      CustomerList_One = null;

      CustomerList_Two = null;
      // CustomerList_One & CustomerList_Two -- (Garbage Collected)
      GC.Collect();

      Console.WriteLine("Total No. of Customers : " +  AllCustomers.Count());
      Console.WriteLine("-------------------------------------------------");
      foreach (var customer in AllCustomers)
      {
         Console.WriteLine("Customer : " + customer);
      }
      Console.WriteLine("-------------------------------------------------");

   }
}
Rehan Shah
quelle
1

Im Sonderfall: "Alle Elemente von List1 gehen in eine neue List2": (zB eine String-Liste)

List<string> list2 = new List<string>(list1);

In diesem Fall wird list2 mit allen Elementen aus list1 generiert.

Arthur Zennig
quelle
0

Sie müssen den Concat-Betrieb verwenden

Artem
quelle
0

Wenn Sie nur wenige Listen haben, aber nicht genau wissen, wie viele, verwenden Sie Folgendes:

listsOfProducts enthält nur wenige mit Objekten gefüllte Listen.

List<Product> productListMerged = new List<Product>();

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
john.kernel
quelle