Combine dos (o más) listas en una, en C # .NET

246

¿Es posible convertir dos o más listas en una sola lista, en .NET usando C #?

Por ejemplo,

public static List<Product> GetAllProducts(int categoryId){ .... }
.
.
.
var productCollection1 = GetAllProducts(CategoryId1);
var productCollection2 = GetAllProducts(CategoryId2);
var productCollection3 = GetAllProducts(CategoryId3);
zekia
fuente
¿Quieres fusionar productCollection1 2 y 3?
¿quieres decir fusionar más de una lista en una lista?
Amr Badawy
Tu ejemplo me confunde ... ¿estás buscando el AddRangemétodo?
Bobby

Respuestas:

457

Puede usar el LINQ Concaty los ToListmétodos:

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

Tenga en cuenta que hay formas más eficientes de hacer esto: lo anterior básicamente recorrerá todas las entradas, creando un búfer de tamaño dinámico. Como puede predecir el tamaño para comenzar, no necesita este tamaño dinámico ... por lo que podría usar:

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

( AddRangeestá especialmente revestido ICollection<T>para mayor eficiencia).

Sin embargo, no tomaría este enfoque a menos que realmente tenga que hacerlo.

Jon Skeet
fuente
44
Por cierto, productCollection1.ForEach(p => allProducts.Add(p))es más eficiente que AddRange.
Marc Climent
8
@MarcCliment: Esa es una declaración general bastante audaz, especialmente cuando AddRangese puede hacer una copia en bloque de una matriz subyacente a otra. ¿Tienes algún enlace para evidencia de esto?
Jon Skeet
44
@MarcCliment: Bien para una cosa, en sus pruebas de que estás no la creación de la lista con el tamaño final correcta - mientras que el código en mi respuesta hace. Haga eso, y la prueba 10000 * 10000 es más rápida usando AddRange, al menos, aunque otros resultados son inconsistentes. (También debe forzar la recolección de basura entre las pruebas, y yo diría que las pruebas muy cortas son insignificantes)
Jon Skeet
66
@MarcCliment: ¿Qué caso particular? Cual CLR? ¿Qué arquitectura de CPU? En mi máquina obtengo una mezcla de resultados. Es por eso que odio decir / aceptar declaraciones generales como "BTW, productionCollection1.ForEach(...)es más eficiente que AddRange". El rendimiento rara vez se describe tan fácilmente. Yo estoy sorprendido de que AddRange no late que cómodamente sin embargo - que necesito para investigar que más.
Jon Skeet
15
He actualizado el gist ( gist.github.com/mcliment/4690433 ) con la prueba de rendimiento usando BenchmarksDotNet y probado adecuadamente, AddRangees la mejor opción para la velocidad bruta (aproximadamente 4x y cuanto más grandes sean las listas, mejor será el aumento), como Jon sugerir
Marc Climent
41

Suponiendo que desea una lista que contenga todos los productos para los ID de categoría especificados, puede tratar su consulta como una proyección seguida de una operación de aplanamiento . Hay un operador de LINQ que hace eso: SelectMany.

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

En C # 4, puede acortar SelectMany a: .SelectMany(GetAllProducts)

Si ya tiene listas que representan los productos para cada Id, entonces lo que necesita es una concatenación , como otros señalan.

Y yo
fuente
8
Si el OP no necesita las listas individuales por cualquier otro motivo, esta es una gran solución.
Jon Skeet
2
Teniendo un problema similar y estaba a punto de rendirme y publicar una pregunta, cuando encontré esto. Esta es la mejor respuesta. Especialmente si el código ya tiene esas categorías en una lista o matriz, lo cual era cierto en mi caso.
22

puedes combinarlos usando LINQ:

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

Sin List.AddRange()embargo, el enfoque más tradicional del uso podría ser más eficiente.

Botz3000
fuente
8

Puede usar el método de extensión Concat :

var result = productCollection1
    .Concat(productCollection2)
    .Concat(productCollection3)
    .ToList();
Darin Dimitrov
fuente
8
list4 = list1.Concat(list2).Concat(list3).ToList();
deciclón
fuente
4

Sé que esta es una vieja pregunta que pensé que podría agregar mis 2 centavos.

Si tienes un List<Something>[]puedes unirte a ellos 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 esto ayude.

sQuir3l
fuente
2

Ya lo he comentado, pero sigo pensando que es una opción válida, solo pruebe si en su entorno es mejor una solución u otra. En mi caso particular, el uso source.ForEach(p => dest.Add(p))funciona mejor que el clásico, AddRangepero no he investigado por qué en el nivel bajo.

Puede ver un código de ejemplo aquí: https://gist.github.com/mcliment/4690433

Entonces la opción sería:

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

Pruébalo para ver si te funciona.

Descargo de responsabilidad : no estoy abogando por esta solución, encuentro Concatla más clara. Simplemente dije, en mi discusión con Jon, que en mi máquina este caso funciona mejor que AddRange, pero él dice, con mucho más conocimiento que yo, que esto no tiene sentido. Ahí está la esencia si quieres comparar.

Marc Climent
fuente
Independientemente de su debate con Jon Skeet en los comentarios, prefiero la limpieza que ofrece su solución propuesta, esto solo agrega una interpretación innecesaria en cuanto a la intención del código.
Vix
1
Creo que la opción más clara es Concat como una generalización de AddRange, semánticamente más correcta y no modifica la lista en el lugar, lo que la hace encadenable. La discusión con Jon fue sobre el rendimiento, no la limpieza.
Marc Climent
Tengo algo como esto: var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();que lo agrega a GridView pero como una columna. Me gustaría dividirlos en tres columnas 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();

De esta manera, es una Lista multidimensional y .SelectMany () lo aplanará en un IEnumerable de producto y luego usaré el método .ToList () después.

Gringo Jaimes
fuente
2

Para fusionar o combinar a listas en una lista de One.

  • Hay una cosa que debe ser cierta: el tipo de ambas listas será igual.

  • Por ejemplo : si tenemos una lista de stringmodo que podamos agregar, agregue otra lista a la lista existente que tenga una lista de tipo cadena; de lo contrario, no podemos.

Ejemplo :

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
fuente
1

En el caso especial: "Todos los elementos de la Lista1 van a una nueva Lista2": (por ejemplo, una lista de cadenas)

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

En este caso, list2 se genera con todos los elementos de list1.

Arthur Zennig
fuente
0

Necesita usar la operación Concat

Artem
fuente
0

Cuando tienes pocas listas pero no sabes cuántas exactamente, usa esto:

listsOfProducts contiene pocas listas llenas de objetos.

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

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