Объединить два (или более) списков в один, в 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.