Hợp nhất hai danh sách (hoặc nhiều hơn) thành một, trong C # .NET


246

Có thể chuyển đổi hai hoặc nhiều danh sách thành một danh sách, trong .NET bằng C # không?

Ví dụ,

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

Bạn có muốn hợp nhất sản phẩmCollection1 2 và 3 không?

bạn có nghĩa là hợp nhất nhiều danh sách trong một danh sách?
Amr Badawy

Ví dụ của bạn làm tôi bối rối ... bạn đang tìm kiếm AddRange-Method?
Bobby

Câu trả lời:


457

Bạn có thể sử dụng LINQ ConcatToListcác phương thức:

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

Lưu ý rằng có nhiều cách hiệu quả hơn để làm điều này - về cơ bản sẽ lặp qua tất cả các mục, tạo ra một bộ đệm có kích thước động. Như bạn có thể dự đoán kích thước để bắt đầu, bạn không cần kích thước động này ... vì vậy bạn có thể sử dụng:

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

( AddRangeđược đặc biệt ICollection<T>cho hiệu quả.)

Tôi sẽ không thực hiện phương pháp này trừ khi bạn thực sự phải làm.


4
BTW, productCollection1.ForEach(p => allProducts.Add(p))có hiệu suất cao hơn AddRange.
Marc Climent

8
@MarcCliment: Đó là một tuyên bố chăn khá táo bạo - đặc biệt là khi AddRangethực hiện sao chép khối từ mảng này sang mảng khác. Bạn có bất kỳ liên kết cho bằng chứng về điều này?
Jon Skeet

4
@MarcCliment: Vâng, vì một điều, trong các thử nghiệm của bạn, bạn không tạo danh sách với kích thước cuối cùng chính xác - trong khi mã trong câu trả lời của tôi thì có. Làm điều đó và thử nghiệm 10000 * 10000 nhanh hơn bằng cách sử dụng AddRange, ít nhất - mặc dù các kết quả khác không nhất quán. (Bạn cũng nên buộc thu gom rác giữa các lần kiểm tra - và tôi cho rằng các thử nghiệm rất ngắn là vô nghĩa nhỏ.)
Jon Skeet

6
@MarcCliment: Trường hợp cụ thể nào? CLR nào? Kiến trúc CPU nào? Trên máy của tôi, tôi nhận được một hỗn hợp các kết quả. Đây là lý do tại sao tôi không thích tuyên bố / chấp nhận các tuyên bố về chăn như "BTW, productionCollection1.ForEach(...)có hiệu suất cao hơn AddRange". Hiệu suất rất hiếm khi được mô tả dễ dàng. Mặc dù vậy, tôi rất ngạc nhiên khi AddRange không đánh bại nó một cách khéo léo - tôi cần điều tra thêm.
Jon Skeet

15
Tôi đã cập nhật ý chính ( gist.github.com/mcliment/4690433 ) với bài kiểm tra hiệu suất bằng cách sử dụng BenchmarkDotNet và được kiểm tra đúng cách, AddRangelà tùy chọn tốt nhất cho tốc độ thô (khoảng 4x và càng lớn thì danh sách càng tăng càng tốt), vì Jon đề nghị.
Marc Climent

41

Giả sử bạn muốn có một danh sách chứa tất cả các sản phẩm cho Id loại được chỉ định, bạn có thể coi truy vấn của mình dưới dạng phép chiếu theo sau là thao tác làm phẳng . Có một toán tử LINQ thực hiện điều đó : SelectMany.

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

Trong C # 4, bạn có thể rút ngắn ChọnMany thành: .SelectMany(GetAllProducts)

Nếu bạn đã có danh sách đại diện cho các sản phẩm cho mỗi Id, thì điều bạn cần là một sự kết hợp , như những người khác chỉ ra.


8
Nếu OP không cần các danh sách riêng lẻ vì bất kỳ lý do nào khác, đây là một giải pháp tuyệt vời.
Jon Skeet

2
Có một vấn đề tương tự và đã từ bỏ và gửi một câu hỏi, khi tôi tìm thấy điều này. Đây là câu trả lời tốt nhất. Đặc biệt là nếu mã đã có các danh mục đó trong một danh sách hoặc mảng, điều đó đúng trong trường hợp của tôi.

22

bạn có thể kết hợp chúng bằng LINQ:

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

cách tiếp cận truyền thống hơn của việc sử dụng List.AddRange()có thể hiệu quả hơn mặc dù.



8

Bạn có thể sử dụng phương thức mở rộng Concat :

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


4

Tôi biết đây là một câu hỏi cũ tôi nghĩ rằng tôi có thể chỉ cần thêm 2 xu của mình.

Nếu bạn có một List<Something>[]bạn có thể tham gia với họ bằng cách sử dụngAggregate

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

    return result;
}

Hi vọng điêu nay co ich.


2

Tôi đã nhận xét nó nhưng tôi vẫn nghĩ là một tùy chọn hợp lệ, chỉ cần kiểm tra xem trong môi trường của bạn tốt hơn giải pháp này hay giải pháp khác. Trong trường hợp cụ thể của tôi, sử dụng hiệu suất source.ForEach(p => dest.Add(p))tốt hơn so với cổ điển AddRangenhưng tôi đã không điều tra lý do tại sao ở mức thấp.

Bạn có thể xem mã ví dụ tại đây: https://gist.github.com/mcliment/4690433

Vì vậy, tùy chọn sẽ là:

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

Kiểm tra nó để xem nếu nó làm việc cho bạn.

Tuyên bố miễn trừ trách nhiệm : Tôi không ủng hộ giải pháp này, tôi thấy giải pháp Concatrõ ràng nhất. Tôi vừa tuyên bố - trong cuộc thảo luận của tôi với Jon - rằng trong máy của tôi, trường hợp này hoạt động tốt hơn AddRange, nhưng anh ta nói, với kiến ​​thức nhiều hơn tôi, rằng điều này không có ý nghĩa gì. Có ý chính nếu bạn muốn so sánh.


Bất kể cuộc tranh luận của bạn với Jon Skeet trong các bình luận, tôi rất thích sự sạch sẽ được cung cấp bởi giải pháp đề xuất của anh ấy, điều này chỉ thêm giải thích không cần thiết như ý định của mã.
Vix

1
Tôi nghĩ rằng tùy chọn rõ ràng nhất là Concat như một khái quát của AddRange, về mặt ngữ nghĩa chính xác hơn và không sửa đổi danh sách tại chỗ khiến nó có thể xâu chuỗi được. Cuộc thảo luận với Jon là về hiệu suất, không phải sự sạch sẽ.
Marc Climent

Tôi có một cái gì đó như thế này: var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();bổ sung nó vào GridView nhưng dưới dạng một cột. Tôi muốn chia chúng thành ba cột riêng biệt.
Si8

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

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

Theo cách này, nó là một Danh sách đa chiều và .SelectMany () sẽ làm phẳng nó thành vô số sản phẩm sau đó tôi sử dụng phương thức .ToList () sau.


2

Để hợp nhất hoặc Kết hợp với Danh sách thành một danh sách.

  • Có một điều phải đúng: loại của cả hai danh sách sẽ bằng nhau.

  • Ví dụ : nếu chúng tôi có danh sách stringđể chúng tôi có thể thêm danh sách khác vào danh sách hiện có danh sách chuỗi loại nếu không chúng tôi không thể.

Ví dụ :

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

Trong trường hợp đặc biệt: "Tất cả các thành phần của List1 chuyển sang List2 mới": (ví dụ: danh sách chuỗi)

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

Trong trường hợp này, list2 được tạo với tất cả các phần tử từ list1.



0

Khi bạn có một vài danh sách nhưng bạn không biết chính xác có bao nhiêu, hãy sử dụng danh sách này:

listsOfProducts chứa một vài danh sách chứa đầy các đối tượng

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

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.