İki (veya daha fazla) listeyi C # .NET ile tek bir liste halinde birleştirme


246

C # kullanarak .NET'te iki veya daha fazla listeyi tek bir listeye dönüştürmek mümkün mü?

Örneğin,

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

Ürün 1 ve 3'ü toplamak istiyor musunuz?

tek bir listede birden fazla listeyi birleştirmek mi demek istediniz?
Amr Badawy

AddRangeÖrneğiniz beni karıştırıyor ... -Method'u mu arıyorsunuz?
Bobby

Yanıtlar:


457

LINQ Concatve ToListyöntemleri kullanabilirsiniz :

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

Bunu yapmanın daha etkili yollarının olduğunu unutmayın - yukarıdakiler temel olarak tüm girişler arasında döngü yaparak dinamik boyutta bir tampon oluşturur. Eğer başlamak boyutunu tahmin edebileceğiniz gibi bu kadar, sen ... Bu dinamik boyutlandırma gerekmez olabilir kullanın:

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

( verimlilik AddRangeiçin özel kasalıdır ICollection<T>.)

Gerçekten de olmadıkça bu yaklaşımı benimsemem.


4
BTW, productCollection1.ForEach(p => allProducts.Add(p))AddRange'den daha performanslıdır.
Marc Climent

8
@MarcCliment: Bu oldukça cesur bir battaniye ifadesi - özellikle de AddRangealttaki diziden diğerine blok kopyasını alırken. Bunun kanıtı için herhangi bir bağlantınız var mı?
Jon Skeet

4
@MarcCliment: Eh bir şey için, sizin testlerde sen ediyoruz değil doğru final boyutu ile listesi oluşturma - cevabım kod yapar oysa. Bunu yapın ve 10000 * 10000 testi AddRange kullanarak daha hızlıdır, ancak diğer sonuçlar tutarsızdır. (Ayrıca testler arasında çöp toplamayı da zorlamalısınız - ve çok kısa testlerin anlamsız derecede küçük olduğunu iddia ediyorum.)
Jon Skeet

6
@MarcCliment: Hangi özel durum? Hangi CLR? Hangi CPU mimarisi? Makinemde sonuçların bir karışımını alıyorum. Bu yüzden "BTW, productionCollection1.ForEach(...)AddRange'den daha performanslı " gibi battaniye ifadelerini kullanmaktan / kabul etmekten nefret ediyorum . Performans çok nadiren çok kolay tanımlanır. Ben am O daha fazla araştırma gerekiyor - gerçi AddRange dezavantaj bunu dayak olmadığını şaşırttı.
Jon Skeet

15
Özü güncelledim (Benist gist.github.com/mcliment/4690433 ) BenchmarksDotNet kullanarak performans testi ile ve düzgün bir şekilde test edildi, AddRangeham hız için en iyi seçenek (yaklaşık 4x ve daha büyük listeler daha iyi artış), Jon olarak suggeste.
Marc Climent

41

Belirtilen kategori kimlikleri için tüm ürünleri içeren bir liste istediğinizi varsayarsak , sorgunuzu yansıtma işlemi ve ardından düzleştirme işlemi olarak ele alabilirsiniz. Bunu yapan bir LINQ operatörü var:SelectMany .

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

C # 4'te SelectMany'yi kısaltabilirsiniz: .SelectMany(GetAllProducts)

Her bir Kimlik için ürünleri temsil eden listeleriniz zaten varsa , diğerlerinin belirttiği gibi, ihtiyacınız olan bir birleştirme .


8
Eğer OP başka bir sebepten dolayı bireysel listelere ihtiyaç duymuyorsa, bu harika bir çözümdür.
Jon Skeet

2
Benzer bir sorun olması ve bunu bulduğumda vazgeçmek ve bir soru göndermek üzereydi. Bu en iyi cevap. Özellikle kod zaten bu kategorileri bir liste veya dizi varsa, benim durumumda doğruysa.

22

LINQ kullanarak bunları birleştirebilirsiniz:

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

daha geleneksel kullanım yaklaşımı List.AddRange()daha verimli olabilir.



8

Concat uzantısı yöntemini kullanabilirsiniz :

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


4

Bu sadece 2 sent ekleyebileceğimi düşündüğüm eski bir soru olduğunu biliyorum.

Eğer bir varsa List<Something>[]kullanmakta onlara katılabilirsinizAggregate

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

    return result;
}

Bu yardımcı olur umarım.


2

Zaten yorum yaptım ama hala geçerli bir seçenek olduğunu düşünüyorum, sadece ortamınızda daha iyi bir çözüm ya da diğer olup olmadığını test edin. Benim özel durumumda, kullanmak source.ForEach(p => dest.Add(p))klasikten daha iyi performans gösteriyorAddRange ancak neden düşük düzeyde olduğunu araştırmadım.

Burada bir örnek kod görebilirsiniz: https://gist.github.com/mcliment/4690433

Yani seçenek şöyle olurdu:

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

İşinize yarayıp yaramadığını görmek için test edin.

Feragatname : Bu çözümü savunmuyorum Concat, en açıkını buluyorum . Jon ile tartışmamda makinemde bu durumun daha iyi performans gösterdiğini söyledim AddRange, ancak benden çok daha fazla bilgi ile bunun mantıklı olmadığını söylüyor. Karşılaştırma yapmak istediğinizde bir öz var.


Yorumlarda Jon Skeet ile tartışmanız ne olursa olsun, önerdiği çözümün sunduğu temizliği çok tercih ediyorum, bu sadece kodun niyeti hakkında gereksiz yorum ekliyor.
Vix

1
Bence en açık seçenek AddRange genellemesi olarak Concat olduğunu, anlamsal olarak daha doğru olduğunu ve yerinde zincirlenebilir hale listesi değiştirmez. Jon'la tartışma, temizlikle değil performansla ilgiliydi.
Marc Climent

Bunun gibi bir şey var: var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();GridView ama bir sütun olarak ekler. Onları üç ayrı sütuna bölmek istiyorum.
Si8

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

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

Bu şekilde çok boyutlu bir Liste ve .SelectMany () onu IEnumerable bir ürüne düzleştirecek, sonra .ToList () yöntemini kullanacağım.


2

Listelerle Birleştirmek veya Bir Listede Birleştirmek için.

  • Doğru olması gereken bir şey var: her iki listenin türü eşit olacaktır.

  • Örneğin : listemiz varsa string, var olan listeye dize türüne sahip listeye başka bir liste ekleyebiliriz, aksi takdirde giremeyiz.

Örnek :

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

Özel durumda: "List1 öğesinin tüm öğeleri yeni bir List2'ye gider": (örneğin bir dize listesi)

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

Bu durumda, list2 , list1'deki tüm öğelerle oluşturulur.



0

Çok az listeniz varsa, ancak tam olarak kaç tane listeyi bilmiyorsanız, bunu kullanın:

listsOfProducts nesnelerle dolu birkaç liste içeriyor.

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

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.