Scal dwie (lub więcej) listy w jedną, w języku C # .NET


246

Czy można przekonwertować dwie lub więcej list na jedną listę w .NET przy użyciu C #?

Na przykład,

public static List<Product> GetAllProducts(int categoryId){ .... }
.
.
.
var productCollection1 = GetAllProducts(CategoryId1);
var productCollection2 = GetAllProducts(CategoryId2);
var productCollection3 = GetAllProducts(CategoryId3);

Czy chcesz scalić productCollection1 2 i 3?

masz na myśli scalenie więcej niż jednej listy na jednej liście?
— Amr Badawy,

Twój przykład mnie dezorientuje ... szukasz AddRangemetody?
— Bobby,

Odpowiedzi:


457

Możesz użyć LINQ Concati ToListmetod:

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

Zauważ, że istnieją bardziej wydajne sposoby na zrobienie tego - powyższe będzie zasadniczo przechodzić przez wszystkie wpisy, tworząc bufor o dynamicznym rozmiarze. Jak można przewidzieć wielkość na początek, nie trzeba tego rozmiaru dynamicznego ... więc mógł używać:

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

( AddRangejest specjalnie zaprojektowany ICollection<T>dla zwiększenia wydajności).

Nie przyjąłbym tego podejścia, chyba że naprawdę musisz.


4
BTW, productCollection1.ForEach(p => allProducts.Add(p))jest bardziej wydajny niż AddRange.
— Marc Climent

8
@MarcCliment: To dość odważna instrukcja kocowa - szczególnie, gdy AddRangemożna wykonać kopię bloku z jednej podstawowej tablicy do drugiej. Czy masz linki do tego dowodu?
— Jon Skeet

4
@MarcCliment: Cóż, po pierwsze, w twoich testach nie tworzysz listy z poprawnym końcowym rozmiarem - podczas gdy kod w mojej odpowiedzi ma. Zrób to, a test 10000 * 10000 jest co najmniej szybszy przy użyciu AddRange - chociaż inne wyniki są niespójne. (Powinieneś także wymusić odśmiecanie między testami - a ja twierdzę, że bardzo krótkie testy są bez znaczenia małe).
— Jon Skeet

6
@MarcCliment: Który konkretny przypadek? Który CLR? Która architektura procesora? Na mojej maszynie otrzymuję mieszankę wyników. Dlatego nie lubię twierdzić / przyjmować instrukcji productionCollection1.ForEach(...)kocowych, takich jak „BTW, jest bardziej wydajny niż AddRange”. Wydajność jest bardzo rzadko tak łatwa do opisania. Ja jestem zaskoczony, że AddRange nie bije jej dłoń choć - muszę zbadać to dalej.
— Jon Skeet

15
Zaktualizowałem gist ( gist.github.com/mcliment/4690433 ) testem wydajności przy użyciu BenchmarksDotNet i odpowiednio przetestowałem, AddRangeto najlepsza opcja dla prędkości początkowej (około 4x i im większa lista, tym większy wzrost), ponieważ Jon sugerować.
— Marc Climent,

41

Zakładając, że potrzebujesz listy zawierającej wszystkie produkty dla określonych kategorii ID, możesz potraktować swoje zapytanie jako rzut, po którym nastąpi operacja spłaszczenia . Jest operatorem LINQ to robi, że: SelectMany.

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

W C # 4 możesz skrócić SelectMany do: .SelectMany(GetAllProducts)

Jeśli masz już listy reprezentujące produkty dla każdego identyfikatora, to potrzebujesz , jak konkatenacja , jak podkreślają inni.


8
Jeśli OP nie potrzebuje indywidualnych list z jakiegokolwiek innego powodu, jest to świetne rozwiązanie.
— Jon Skeet,

2
Miałem podobny problem i miałem zamiar zrezygnować i zadać pytanie, kiedy to znalazłem. To najlepsza odpowiedź. Zwłaszcza jeśli kod ma już te kategorie na liście lub tablicy, co było prawdą w moim przypadku.

22

możesz je połączyć za pomocą LINQ:

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

bardziej tradycyjne podejście List.AddRange()może być jednak bardziej wydajne.



8

Możesz użyć metody rozszerzenia Concat :

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


4

Wiem, że to stare pytanie, pomyślałem, że mogę dodać 2 centy.

Jeśli masz List<Something>[], możesz do nich dołączyć za pomocąAggregate

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

    return result;
}

Mam nadzieję że to pomoże.


2

Już to skomentowałem, ale nadal uważam, że jest to prawidłowa opcja, po prostu sprawdź, czy w twoim środowisku jest lepsze jedno lub drugie rozwiązanie. W moim szczególnym przypadku używanie source.ForEach(p => dest.Add(p))działa lepiej niż klasyczny, AddRangeale nie badałem, dlaczego na niskim poziomie.

Przykładowy kod możesz zobaczyć tutaj: https://gist.github.com/mcliment/4690433

Tak więc opcją byłoby:

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));

Przetestuj, aby sprawdzić, czy to działa dla Ciebie.

Oświadczenie : Nie opowiadam się za tym rozwiązaniem, uważam, że jest Concatto najbardziej jasne. Właśnie powiedziałem - w mojej dyskusji z Jonem - że w mojej maszynie ta sprawa działa lepiej niż AddRange, ale on, przy znacznie większej wiedzy niż ja, że ​​to nie ma sensu. Jest sedno, jeśli chcesz porównać.


Niezależnie od twojej debaty z Jonem Skeetem w komentarzach, zdecydowanie wolę czystość oferowaną przez jego proponowane rozwiązanie, to tylko dodaje niepotrzebnej interpretacji co do intencji kodu.
— Vix,

1
Myślę, że najbardziej przejrzystą opcją jest Concat jako uogólnienie AddRange, semantycznie bardziej poprawne i nie modyfikujące listy w miejscu, co czyni ją łańcuchowalną. Dyskusja z Jonem dotyczyła wydajności, a nie czystości.
— Marc Climent

Mam coś takiego: var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();co dodaje to do GridView, ale jako jedną kolumnę. Chciałbym podzielić je na trzy osobne kolumny.
— Si8,

2
// I would make it a little bit more simple

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

W ten sposób jest to wielowymiarowa lista, a .SelectMany () spłaszczy ją do IEnumerable produktu, a następnie użyję metody .ToList ().


2

Aby scalić lub połączyć z listami w jedną listę.

  • Jest jedna rzecz, która musi być prawdziwa: typ obu list będzie równy.

  • Na przykład : jeśli mamy listę, stringwięc możemy dodać kolejną listę do istniejącej listy, która ma listę ciągów znaków, inaczej nie możemy.

Przykład :

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("-------------------------------------------------");

   }
}

1

W szczególnym przypadku: „Wszystkie elementy List1 trafiają do nowej List2”: (np. Lista ciągów)

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

W takim przypadku lista 2 jest generowana ze wszystkimi elementami z listy 1.



0

Gdy masz kilka list, ale nie wiesz ile dokładnie, użyj tego:

listsOfProducts zawiera kilka list wypełnionych obiektami.

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

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.