Mesclar duas (ou mais) listas em uma, no C # .NET


246

É possível converter duas ou mais listas em uma única lista, no .NET usando C #?

Por exemplo,

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

Deseja mesclar productCollection1 2 e 3?

você quer dizer mesclar mais de uma lista em uma lista?
Amr Badawy

Seu exemplo está me confundindo ... você está procurando o AddRange-Método?
Bobby

Respostas:


457

Você pode usar o LINQ Concate os ToListmétodos:

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

Observe que existem maneiras mais eficientes de fazer isso - o que foi dito acima basicamente percorre todas as entradas, criando um buffer de tamanho dinâmico. Como você pode prever o tamanho, para começar, não precisa desse dimensionamento dinâmico ... para poder usar:

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

( AddRangeé especialmente indicado ICollection<T>para eficiência.)

Eu não adotaria essa abordagem a menos que você realmente precise.


4
BTW, productCollection1.ForEach(p => allProducts.Add(p))é mais eficiente que AddRange.
Marc Climent

8
@MarcCliment: Essa é uma declaração geral bastante ousada - especialmente quando você AddRangefaz uma cópia de bloco de uma matriz subjacente para outra. Você tem algum link para evidência disso?
precisa saber é o seguinte

4
@ MarcCliment: Bem, por um lado, em seus testes você não está criando a lista com o tamanho final correto - enquanto o código na minha resposta sim. Faça isso, e o teste 10000 * 10000 é mais rápido usando AddRange, pelo menos - embora outros resultados sejam inconsistentes. (Você também deve forçar a coleta de lixo entre os testes - e eu diria que os testes muito curtos são meaninglessly pequena.)
Jon Skeet

6
@ MarcCliment: Qual caso em particular? Qual CLR? Qual arquitetura de CPU? Na minha máquina, recebo uma mistura de resultados. É por isso que detesto declarar / aceitar declarações gerais como "BTW, productionCollection1.ForEach(...)é mais eficiente que AddRange". O desempenho é muito raramente descrito com tanta facilidade. Eu estou surpreso que AddRange não está batendo-o com folga embora - Eu preciso investigar isso ainda mais.
91313 Jon Skeet

15
Atualizei o gist ( gist.github.com/mcliment/4690433 ) com o teste de desempenho usando o BenchmarksDotNet e testado adequadamente, AddRangeé a melhor opção para velocidade bruta (cerca de 4x e quanto maior a lista, melhor o aumento), como Jon suggeste.
Marc Climent

41

Supondo que você queira uma lista contendo todos os produtos para os IDs de categoria especificados, você pode tratar sua consulta como uma projeção seguida por uma operação de nivelamento . Há um operador LINQ que faz isso: SelectMany.

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

No C # 4, você pode reduzir o SelectMany para: .SelectMany(GetAllProducts)

Se você já possui listas representando os produtos para cada ID, o que você precisa é de uma concatenação , como outros apontam.


8
Se o OP não precisar das listas individuais por qualquer outro motivo, essa é uma ótima solução.
quer

2
Tendo um problema semelhante e estava prestes a desistir e postar uma pergunta, quando eu encontrei isso. Esta é a melhor resposta. Especialmente se o código já tiver essas categorias em uma lista ou matriz, o que era verdade no meu caso.

22

você pode combiná-los usando o LINQ:

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

a abordagem mais tradicional do uso List.AddRange()pode ser mais eficiente.



8

Você pode usar o método de extensão Concat :

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


4

Eu sei que esta é uma pergunta antiga que eu pensei em adicionar apenas meus 2 centavos.

Se você tiver um, List<Something>[]pode se juntar a eles usandoAggregate

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

    return result;
}

Espero que isto ajude.


2

Eu já comentei, mas ainda acho que é uma opção válida, apenas teste se no seu ambiente é melhor uma solução ou outra. No meu caso particular, o uso source.ForEach(p => dest.Add(p))é melhor que o clássico, AddRangemas não investiguei o porquê no nível baixo.

Você pode ver um código de exemplo aqui: https://gist.github.com/mcliment/4690433

Portanto, a opção seria:

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

Teste para ver se funciona para você.

Isenção de responsabilidade : não estou advogando por esta solução, acho Concata mais clara. Acabei de afirmar - na minha discussão com Jon - que, na minha máquina, esse caso tem um desempenho melhor do que AddRange, mas ele diz, com muito mais conhecimento do que eu, que isso não faz sentido. Existe a essência se você quiser comparar.


Independentemente do seu debate com Jon Skeet nos comentários, eu prefiro a limpeza oferecida por sua solução proposta, isso apenas adiciona interpretações desnecessárias quanto à intenção do código.
Vix

1
Eu acho que a opção mais clara é o Concat como uma generalização do AddRange, semanticamente mais correta e não modifica a lista no local, o que a torna encadeada. A discussão com Jon foi sobre desempenho, não limpeza.
Marc Climent

Eu tenho algo parecido com isto: o var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();que o adiciona ao GridView, mas como uma coluna. Eu gostaria de dividi-los em três colunas separadas.
Si8

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

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

Dessa forma, é uma lista multidimensional e o .SelectMany () o achatará em um IEnumerable de produto e, depois, uso o método .ToList ().


2

Para mesclar ou Combinar para listas em uma lista Um.

  • Há uma coisa que deve ser verdadeira: o tipo de ambas as listas será igual.

  • Por exemplo : se tivermos lista de string, podemos adicionar uma outra lista à lista existente, que possui uma lista do tipo string, caso contrário, não podemos.

Exemplo :

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

No caso especial: "Todos os elementos da Lista1 vão para uma nova Lista2": (por exemplo, uma lista de cadeias)

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

Nesse caso, a lista2 é gerada com todos os elementos da lista1.



0

Quando você tiver poucas listas, mas não souber exatamente quantas, use o seguinte:

listsOfProducts contém poucas listas preenchidas com objetos.

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

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.