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);
zekia
fonte
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.

Jon Skeet
fonte
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.

Ani
fonte
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.

Botz3000
fonte
11

Dê uma olhada em List.AddRange para mesclar listas

StuartLC
fonte
8

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

var result = productCollection1
    .Concat(productCollection2)
    .Concat(productCollection3)
    .ToList();
Darin Dimitrov
fonte
8
list4 = list1.Concat(list2).Concat(list3).ToList();
deciclone
fonte
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.

sQuir3l
fonte
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.

Marc Climent
fonte
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 ().

Gringo Jaimes
fonte
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("-------------------------------------------------");

   }
}
Rehan Shah
fonte
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.

Arthur Zennig
fonte
0

Você precisa usar a operação Concat

Artem
fonte
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)));
john.kernel
fonte