Unisci due (o più) elenchi in uno, in C # .NET


246

È possibile convertire due o più elenchi in un unico elenco, in .NET usando C #?

Per esempio,

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

Vuoi unire productCollection1 2 e 3?

vuoi dire unire più di un elenco in un elenco?
Amr Badawy,

Il tuo esempio mi sta confondendo ... stai cercando il AddRangemetodo?
Bobby,

Risposte:


457

È possibile utilizzare LINQ Concate ToListmetodi:

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

Si noti che esistono modi più efficienti per eseguire questa operazione: quanto sopra sostanzialmente eseguirà il ciclo di tutte le voci, creando un buffer di dimensioni dinamiche. Come si può prevedere le dimensioni per cominciare, non è necessario questo dimensionamento dinamico ... così si potrebbe usare:

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

( AddRangeè speciale ICollection<T>per efficienza.)

Non prenderei questo approccio a meno che tu non debba davvero farlo.


4
A proposito, productCollection1.ForEach(p => allProducts.Add(p))è più performante di AddRange.
Marc Climent,

8
@MarcCliment: questa è un'affermazione coperta piuttosto audace, specialmente per quanto AddRangeriguarda la copia a blocchi da un array sottostante a un altro. Hai qualche link per la prova di questo?
Jon Skeet,

4
@MarcCliment: Beh, per prima cosa, nei test si sta non creare la lista con la dimensione finale corretto -, mentre il codice nella mia risposta fa. Fatelo e il test 10000 * 10000 è più veloce usando AddRange, almeno - sebbene altri risultati siano incoerenti. (Dovresti anche forzare la raccolta dei rifiuti tra i test - e direi che i test molto brevi sono insignificanti piccoli.)
Jon Skeet

6
@MarcCliment: quale caso particolare? Quale CLR? Quale architettura CPU? Sulla mia macchina ottengo una combinazione di risultati. Questo è il motivo per cui detesto affermare / accettare dichiarazioni productionCollection1.ForEach(...)generali come "A proposito, è più performante di AddRange". Le prestazioni sono molto raramente così facilmente descritte. Io sto sorpreso che AddRange non batte lo comodamente anche se - ho bisogno di indagare che ulteriori.
Jon Skeet,

15
Ho aggiornato l'essenza ( gist.github.com/mcliment/4690433 ) con il test delle prestazioni utilizzando BenchmarksDotNet e correttamente testato, AddRangeè l'opzione migliore per la velocità non elaborata (circa 4x e maggiore è l'elenco, migliore è l'aumento), come Jon suggeste.
Marc Climent,

41

Supponendo che si desideri un elenco contenente tutti i prodotti per gli ID categoria specificati, è possibile considerare la query come una proiezione seguita da un'operazione di appiattimento . C'è un operatore di LINQ che lo fa: SelectMany.

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

In C # 4, puoi accorciare SelectMany per: .SelectMany(GetAllProducts)

Se hai già elenchi che rappresentano i prodotti per ciascun ID, ciò di cui hai bisogno è una concatenazione , come altri sottolineano.


8
Se l'OP non ha bisogno dei singoli elenchi per nessun altro motivo, questa è un'ottima soluzione.
Jon Skeet,

2
Ho riscontrato un problema simile e stavo per rinunciare e pubblicare una domanda, quando l'ho trovato. Questa è la risposta migliore Soprattutto se il codice ha già quelle categorie in un elenco o in un array, il che era vero nel mio caso.

22

puoi combinarli usando LINQ:

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

l'approccio più tradizionale di utilizzo List.AddRange()potrebbe essere più efficiente però.



8

È possibile utilizzare il metodo di estensione Concat :

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


4

So che questa è una vecchia domanda che pensavo di poter aggiungere solo i miei 2 centesimi.

Se ne hai uno List<Something>[]puoi unirti a loro 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;
}

Spero che questo ti aiuti.


2

L'ho già commentato ma continuo a pensare che sia un'opzione valida, basta provare se nel tuo ambiente è meglio una soluzione o l'altra. Nel mio caso particolare, l'utilizzo source.ForEach(p => dest.Add(p))funziona meglio del classico AddRangema non ho studiato il perché a basso livello.

Puoi vedere un codice di esempio qui: https://gist.github.com/mcliment/4690433

Quindi l'opzione sarebbe:

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

Provalo per vedere se funziona per te.

Disclaimer : non sto sostenendo questa soluzione, trovo Concatla più chiara. Ho appena affermato, nella mia discussione con Jon, che nella mia macchina questo caso funziona meglio di AddRange, ma dice, con molta più conoscenza di me, che questo non ha senso. C'è l'essenza se vuoi confrontare.


Indipendentemente dal tuo dibattito con Jon Skeet nei commenti, preferisco di gran lunga la pulizia offerta dalla sua soluzione proposta, ciò aggiunge semplicemente un'interpretazione non necessaria sull'intenzione del codice.
Vix,

1
Penso che l'opzione più chiara sia Concat come generalizzazione di AddRange, semanticamente più corretta e non modifica l'elenco sul posto che lo rende concatenabile. La discussione con Jon riguardava le prestazioni, non la pulizia.
Marc Climent,

Ho qualcosa del genere: var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();che lo aggiunge a GridView ma come una colonna. Vorrei dividerli in tre colonne separate.
Si8,

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

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

In questo modo si tratta di un elenco multidimensionale e .SelectMany () lo appiattirà in un prodotto IEnumerable, quindi utilizzo il metodo .ToList ().


2

Per unire o combinare a elenchi in un unico elenco.

  • C'è una cosa che deve essere vera: il tipo di entrambi gli elenchi sarà uguale.

  • Per esempio : se abbiamo un elenco di stringcosì possiamo aggiungere aggiungere un altro elenco all'elenco esistente che ha un elenco di tipo stringa altrimenti non possiamo.

Esempio :

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

Nel caso speciale: "Tutti gli elementi di List1 passano a un nuovo List2": (ad esempio un elenco di stringhe)

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

In questo caso, list2 viene generato con tutti gli elementi di list1.


0

È necessario utilizzare l'operazione Concat


0

Quando hai qualche elenco ma non sai quanti esattamente, usa questo:

listsOfProducts contiene alcuni elenchi pieni di oggetti.

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

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.