Объединить два (или более) списков в один, в C # .NET


246

Можно ли преобразовать два или более списков в один список в .NET с помощью C #?

Например,

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

Хотите объединить productCollection1 2 и 3?

Вы имеете в виду объединить более одного списка в один список?
Амр Бадави

Твой пример меня смущает ... ты ищешь AddRangeметод?
Бобби

Ответы:


457

Вы можете использовать LINQ Concatи ToListметоды:

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

Обратите внимание, что есть более эффективные способы сделать это - вышеупомянутое будет в основном перебирать все записи, создавая буфер динамического размера. Поскольку вы можете предсказать размер для начала, вам не нужен этот динамический размер ... поэтому вы можете использовать:

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

( AddRangeСпециально для ICollection<T>эффективности.)

Я бы не стал использовать этот подход, если вы действительно не должны.


4
Кстати, productCollection1.ForEach(p => allProducts.Add(p))более производительный, чем AddRange.
Марк Климент

8
@MarcCliment: Это довольно смелое общее утверждение, особенно когда AddRangeнужно сделать копию блока из одного базового массива в другой. У вас есть ссылки для доказательства этого?
Джон Скит

4
@MarcCliment: Во-первых, в ваших тестах вы не создаете список с правильным окончательным размером - в отличие от кода в моем ответе. Сделайте это, и тест 10000 * 10000 будет быстрее, по крайней мере, с использованием AddRange - хотя другие результаты противоречивы. (Вы также должны форсировать сборку мусора между тестами - и я бы сказал, что очень короткие тесты бессмысленно малы.)
Джон Скит

6
@MarcCliment: какой конкретный случай? Какой CLR? Какая архитектура процессора? На моей машине я получаю смесь результатов. Вот почему я не хочу заявлять / принимать общие утверждения, такие как «Кстати, productionCollection1.ForEach(...)более производительный, чем AddRange». Производительность очень редко так легко описывается. Я буду удивлен , что AddRange не бить его удобным , хотя - мне нужно исследовать , что дальше.
Джон Скит

15
Я обновил суть ( gist.github.com/mcliment/4690433 ) с помощью теста производительности с использованием BenchmarksDotNet и надлежащим образом протестирован. AddRangeЭто лучший вариант для необработанной скорости (примерно в 4 раза и чем больше списки, тем лучше увеличение), как Джон suggeste.
Марк Климент

41

Предполагая, что вам нужен список, содержащий все продукты для указанных идентификаторов категорий, вы можете рассматривать ваш запрос как проекцию, за которой следует операция выравнивания . Там есть оператор LINQ , что делает это: SelectMany.

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

В C # 4 вы можете сократить SelectMany, чтобы: .SelectMany(GetAllProducts)

Если у вас уже есть списки, представляющие продукты для каждого идентификатора, то вам нужно объединение , как указывают другие.


8
Если ОП по каким-либо другим причинам не нужны отдельные списки, это отличное решение.
Джон Скит

2
Возникла похожая проблема, и я собирался сдаться и опубликовать вопрос, когда обнаружил это. Это лучший ответ. Особенно, если код уже имеет эти категории в списке или массиве, что было верно в моем случае.

22

Вы можете объединить их, используя LINQ:

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

List.AddRange()хотя более традиционный подход к использованию может быть более эффективным.





4

Я знаю, что это старый вопрос, я думал, я мог бы просто добавить свои 2 цента.

Если у вас есть, List<Something>[]вы можете присоединиться к ним, используя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;
}

Надеюсь это поможет.


2

Я уже прокомментировал это, но я все еще думаю, что это верный вариант, просто проверьте, если в вашей среде лучше одно или другое решение. В моем конкретном случае использование source.ForEach(p => dest.Add(p))работает лучше, чем классический, AddRangeно я не исследовал, почему на низком уровне.

Вы можете увидеть пример кода здесь: https://gist.github.com/mcliment/4690433

Таким образом, вариант будет:

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

Проверьте это, чтобы увидеть, работает ли он для вас.

Отказ от ответственности : я не защищаю это решение, я нахожу Concatсамое ясное. Я только что сказал - в моей беседе с Джоном - что в моем аппарате этот случай работает лучше, чем AddRange, но он говорит, с гораздо большим знанием, чем я, что это не имеет смысла. Там есть суть, если вы хотите сравнить.


Независимо от вашей дискуссии с Джоном Скитом в комментариях, я предпочитаю чистоту, предложенную его предложенным решением, это просто добавляет ненужную интерпретацию намерению кода.
Vix

1
Я думаю, что наиболее понятным вариантом является Concat как обобщение AddRange, семантически более правильный и не изменяющий список на месте, что делает его цепным. Дискуссия с Джоном была о производительности, а не чистоте.
Марк Климент

У меня есть что-то вроде этого: var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();который добавляет его в GridView, но в виде одного столбца. Я хотел бы разделить их на три отдельные колонки.
Si8

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

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

Таким образом, это многомерный List, и .SelectMany () сведет его в IEnumerable продукта, после чего я использую метод .ToList ().


2

Объединить или объединить в списки в один список.

  • Есть одна вещь, которая должна быть правдой: тип обоих списков будет одинаковым.

  • Например : если у нас есть список, stringмы можем добавить еще один список к существующему списку, у которого есть список строк типа, в противном случае мы не можем.

Пример :

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

В особом случае: «Все элементы List1 переходят в новый List2»: (например, список строк)

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

В этом случае list2 генерируется со всеми элементами из list1.



0

Когда у вас есть несколько списков, но вы не знаете, сколько именно, используйте это:

listsOfProducts содержит несколько списков, заполненных объектами.

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

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.