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);
zekia
źródło
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.

Jon Skeet
źródło
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.

Ani
źródło
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.

Botz3000
źródło
11

Spójrz na List.AddRange, aby scalić listy

StuartLC
źródło
8

Możesz użyć metody rozszerzenia Concat :

var result = productCollection1
    .Concat(productCollection2)
    .Concat(productCollection3)
    .ToList();
Darin Dimitrov
źródło
8
list4 = list1.Concat(list2).Concat(list3).ToList();
decyklon
źródło
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.

sQuir3l
źródło
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ć.

Marc Climent
źródło
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 ().

Gringo Jaimes
źródło
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("-------------------------------------------------");

   }
}
Rehan Shah
źródło
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.

Arthur Zennig
źródło
0

Musisz użyć operacji Concat

Artem
źródło
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)));
john.kernel
źródło