Fusionner deux (ou plusieurs) listes en une seule, en C # .NET

246

Est-il possible de convertir deux ou plusieurs listes en une seule liste, dans .NET en utilisant C #?

Par exemple,

public static List<Product> GetAllProducts(int categoryId){ .... }
.
.
.
var productCollection1 = GetAllProducts(CategoryId1);
var productCollection2 = GetAllProducts(CategoryId2);
var productCollection3 = GetAllProducts(CategoryId3);
zekia
la source
Voulez-vous fusionner productCollection1 2 et 3?
voulez-vous dire fusionner plus d'une liste en une seule liste?
Amr Badawy
Votre exemple me AddRangedéroute ... cherchez-vous la -Méthode?
Bobby

Réponses:

457

Vous pouvez utiliser le LINQ Concatet les ToListméthodes:

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

Notez qu'il existe des moyens plus efficaces de le faire - ce qui précède fera essentiellement une boucle à travers toutes les entrées, créant un tampon de taille dynamique. Comme vous pouvez prédire la taille pour commencer, vous n'avez pas besoin de ce dimensionnement dynamique ... vous pouvez donc utiliser:

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

( AddRangeest dans un boîtier spécial pour plus ICollection<T>d'efficacité.)

Je n'adopterais pas cette approche à moins que vous ne deviez vraiment le faire.

Jon Skeet
la source
4
BTW, productCollection1.ForEach(p => allProducts.Add(p))est plus performant que AddRange.
Marc Climent
8
@MarcCliment: C'est une déclaration de couverture assez audacieuse - d'autant plus que l' AddRangeon peut faire une copie de bloc d'un tableau sous-jacent à un autre. Avez-vous des liens pour en témoigner?
Jon Skeet
4
@MarcCliment: Eh bien pour une chose, dans vos tests, vous ne créez pas la liste avec la taille finale correcte - contrairement au code dans ma réponse. Faites cela, et le test 10000 * 10000 est plus rapide en utilisant AddRange, au moins - bien que les autres résultats ne soient pas cohérents. (Vous devez également forcer la collecte des ordures entre les tests - et je dirais que les tests très courts sont insignifiants.)
Jon Skeet
6
@MarcCliment: Quel cas particulier? Quel CLR? Quelle architecture CPU? Sur ma machine, j'obtiens un mélange de résultats. C'est pourquoi je répugne à déclarer / accepter des déclarations générales telles que "BTW, productionCollection1.ForEach(...)est plus performant que AddRange". Les performances sont très rarement décrites aussi facilement. Je suis surpris cependant que AddRange ne le bat pas facilement - je dois approfondir cela.
Jon Skeet
15
J'ai mis à jour l'essentiel ( gist.github.com/mcliment/4690433 ) avec le test de performance en utilisant BenchmarksDotNet et correctement testé, AddRangeest la meilleure option pour la vitesse brute (environ 4x et plus les listes sont grandes, meilleure est l'augmentation), comme Jon suggeste.
Marc Climent du
41

En supposant que vous souhaitiez une liste contenant tous les produits pour les identifiants de catégorie spécifiés, vous pouvez traiter votre requête comme une projection suivie d'une opération d' aplatissement . Il y a un opérateur LINQ qui fait que: SelectMany.

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

En C # 4, vous pouvez raccourcir SelectMany pour: .SelectMany(GetAllProducts)

Si vous avez déjà des listes représentant les produits pour chaque ID, alors ce dont vous avez besoin est une concaténation , comme d'autres le soulignent.

Ani
la source
8
Si l'OP n'a pas besoin des listes individuelles pour toute autre raison, c'est une excellente solution.
Jon Skeet
2
Ayant un problème similaire et était sur le point d'abandonner et de poser une question, quand j'ai trouvé cela. C'est la meilleure réponse. Surtout si le code a déjà ces catégories dans une liste ou un tableau, ce qui était vrai dans mon cas.
22

vous pouvez les combiner en utilisant LINQ:

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

l'approche plus traditionnelle de l'utilisation List.AddRange()pourrait cependant être plus efficace.

Botz3000
la source
11

Jetez un œil à List.AddRange pour fusionner les listes

StuartLC
la source
8

Vous pouvez utiliser la méthode d'extension Concat :

var result = productCollection1
    .Concat(productCollection2)
    .Concat(productCollection3)
    .ToList();
Darin Dimitrov
la source
8
list4 = list1.Concat(list2).Concat(list3).ToList();
décyclone
la source
4

Je sais que c'est une vieille question que je pensais pouvoir ajouter mes 2 cents.

Si vous en avez un, List<Something>[]vous pouvez les rejoindre en utilisantAggregate

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

    return result;
}

J'espère que cela t'aides.

sQuir3l
la source
2

Je l'ai déjà commenté mais je pense toujours que c'est une option valide, testez simplement si dans votre environnement est meilleure une solution ou l'autre. Dans mon cas particulier, l'utilisation source.ForEach(p => dest.Add(p))est plus performante que le classique, AddRangemais je n'ai pas cherché à savoir pourquoi au niveau bas.

Vous pouvez voir un exemple de code ici: https://gist.github.com/mcliment/4690433

L'option serait donc:

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

Testez-le pour voir si cela fonctionne pour vous.

Avertissement : je ne préconise pas cette solution, je trouve Concatla plus claire. Je viens de dire - dans ma discussion avec Jon - que dans ma machine, ce cas fonctionne mieux que AddRange, mais il dit, avec beaucoup plus de connaissances que moi, que cela n'a pas de sens. Il y a l'essentiel si vous voulez comparer.

Marc Climent
la source
Indépendamment de votre débat avec Jon Skeet dans les commentaires, je préfère de beaucoup la propreté offerte par sa solution proposée, cela ajoute simplement une interprétation inutile quant à l'intention du code.
Vix
1
Je pense que l'option la plus claire est Concat en tant que généralisation d'AddRange, sémantiquement plus correcte et ne modifie pas la liste en place, ce qui la rend chaînable. La discussion avec Jon portait sur la performance, pas sur la propreté.
Marc Climent
J'ai quelque chose comme ça: var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();qui l'ajoute à la GridView mais en une seule colonne. Je voudrais les diviser en trois colonnes distinctes.
Si8
2
// I would make it a little bit more simple

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

De cette façon, il s'agit d'une liste multidimensionnelle et le .SelectMany () l'aplatira en un produit IEnumerable, puis j'utiliserai la méthode .ToList () après.

Gringo Jaimes
la source
2

Pour fusionner ou combiner en listes dans une seule liste.

  • Il y a une chose qui doit être vraie: le type des deux listes sera égal.

  • Par exemple : si nous avons une liste de stringsorte que nous pouvons ajouter ajouter une autre liste à la liste existante qui a une liste de type chaîne sinon nous ne pouvons pas.

Exemple :

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
la source
1

Dans le cas particulier: "Tous les éléments de List1 vont dans un nouveau List2": (par exemple une liste de chaînes)

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

Dans ce cas, list2 est généré avec tous les éléments de list1.

Arthur Zennig
la source
0

Vous devez utiliser l'opération Concat

Artem
la source
0

Lorsque vous avez quelques listes mais que vous ne savez pas exactement combien, utilisez ceci:

listsOfProducts contient quelques listes remplies d'objets.

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

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
john.kernel
la source