Diferencia entre Select y SelectMany

1074

He estado buscando la diferencia entre Selecty SelectManypero no he podido encontrar una respuesta adecuada. Necesito aprender la diferencia cuando uso LINQ To SQL, pero todo lo que he encontrado son ejemplos de matriz estándar.

¿Alguien puede proporcionar un ejemplo LINQ To SQL?

Tarik
fuente
8
puede mirar el código para SelectMany con una función, o con dos funciones referenciasource.microsoft.com/#System.Core/System/Linq/…
barlop
1
Si está familiarizado con Kotlin, tiene implementaciones bastante similares para colecciones como map aka C # Select y flatMap aka C # SelectMany. Básicamente, las funciones de extensión de la biblioteca estándar de Kotlin para colecciones tienen similitud con la biblioteca C # Linq.
Arsenius

Respuestas:

1622

SelectManyaplana las consultas que devuelven listas de listas. Por ejemplo

public class PhoneNumber
{
    public string Number { get; set; }
}

public class Person
{
    public IEnumerable<PhoneNumber> PhoneNumbers { get; set; }
    public string Name { get; set; }
}

IEnumerable<Person> people = new List<Person>();

// Select gets a list of lists of phone numbers
IEnumerable<IEnumerable<PhoneNumber>> phoneLists = people.Select(p => p.PhoneNumbers);

// SelectMany flattens it to just a list of phone numbers.
IEnumerable<PhoneNumber> phoneNumbers = people.SelectMany(p => p.PhoneNumbers);

// And to include data from the parent in the result: 
// pass an expression to the second parameter (resultSelector) in the overload:
var directory = people
   .SelectMany(p => p.PhoneNumbers,
               (parent, child) => new { parent.Name, child.Number });

Demostración en vivo en .NET Fiddle

Mike dos
fuente
1
Pregunta relacionada sobre la anidación Seleccione Muchos para aplanar una estructura jerárquica anidada.
The Red Pea
1
Para comprender el resultadoSelector más El siguiente enlace ayuda a blogs.interknowlogy.com/2008/10/10/…
jamir
Una demostración más con los resultados de los padres: dotnetfiddle.net/flcdCC
Evgeniy Kosjakov
gracias por el enlace de violín!
Aerin
197

Seleccionar muchos es como una operación de unión cruzada en SQL donde toma el producto cruzado.
Por ejemplo si tenemos

Set A={a,b,c}
Set B={x,y}

Seleccionar muchos se puede utilizar para obtener el siguiente conjunto

{ (x,a) , (x,b) , (x,c) , (y,a) , (y,b) , (y,c) }

Tenga en cuenta que aquí tomamos todas las combinaciones posibles que se pueden hacer de los elementos del conjunto A y el conjunto B.

Aquí hay un ejemplo de LINQ que puedes probar

List<string> animals = new List<string>() { "cat", "dog", "donkey" };
List<int> number = new List<int>() { 10, 20 };

var mix = number.SelectMany(num => animals, (n, a) => new { n, a });

la mezcla tendrá los siguientes elementos en estructura plana como

{(10,cat), (10,dog), (10,donkey), (20,cat), (20,dog), (20,donkey)}
Sriwantha Attanayake
fuente
44
Sé que esto es viejo, pero quería agradecerte por esto, ¡me salvó mucho! :) Puede ser útil tener una referencia a esos códigos también: stackoverflow.com/questions/3479980/… ¡Salud!
user3439065
44
SelectMany no tiene que usarse así. Tiene la opción de tomar una sola función también.
barlop
2
No sé si es correcto decir que así es como SelectMany es . Más bien, esta es una forma que SelectManyse puede usar, pero en realidad no es la forma normal de usarlo.
Dave Cousineau
1
Esta fue la respuesta más simple para mí entender.
Chaim Eliyah el
Sería bueno si también demuestras la Wherecondición después de SelectMany
Nitin Kt
126

ingrese la descripción de la imagen aquí

var players = db.SoccerTeams.Where(c => c.Country == "Spain")
                            .SelectMany(c => c.players);

foreach(var player in players)
{
    Console.WriteLine(player.LastName);
}
  1. De Gea
  2. Alba
  3. Costa
  4. Villa
  5. Busquets

...

AlejandroR
fuente
99
datos de gran ejemplo
ben_mj
2
¿podría agregar un ejemplo para seleccionar para completar esta respuesta :)
Harry
73

SelectMany()le permite contraer una secuencia multidimensional de una manera que de otro modo requeriría un segundo Select()o bucle.

Más detalles en esta publicación de blog .

Michael Petrotta
fuente
¿Pero el primero devuelve el tipo de niños Enumerables el segundo ejemplo devuelve el tipo de padres? En realidad estoy un poco confundido, ¿lo abrirías un poco más?
Tarik
Al revés, en realidad. El segundo aplanará por completo la jerarquía de los enumerables, para que pueda recuperar a los niños. Pruebe el artículo en el enlace que agregué, vea si eso ayuda.
Michael Petrotta
El primero no parece ser legal. Creo que el cartel se confundió a sí mismo. El segundo devolvería un número enumerable de padres.
mqp
Gracias, bueno, en realidad sí, los ejemplos fueron un poco confusos aunque :) pero gracias de nuevo por intentar ayudarme.
Tarik
37

Hay varias sobrecargas a SelectMany. Uno de ellos le permite realizar un seguimiento de cualquier relación entre padres e hijos mientras recorre la jerarquía.

Ejemplo : supongamos que tiene la siguiente estructura: League -> Teams -> Player.

Puede devolver fácilmente una colección plana de jugadores. Sin embargo, puede perder cualquier referencia al equipo del que forma parte el jugador.

Afortunadamente hay una sobrecarga para tal fin:

var teamsAndTheirLeagues = 
         from helper in leagues.SelectMany
               ( l => l.Teams
                 , ( league, team ) => new { league, team } )
                      where helper.team.Players.Count > 2 
                           && helper.league.Teams.Count < 10
                           select new 
                                  { LeagueID = helper.league.ID
                                    , Team = helper.team 
                                   };

El ejemplo anterior está tomado del blog Dan's IK . Le recomiendo que lo eche un vistazo.

roland
fuente
19

Entiendo SelectManytrabajar como un atajo de combinación.

Así que puedes:

var orders = customers
             .Where(c => c.CustomerName == "Acme")
             .SelectMany(c => c.Orders);
Nathan Koop
fuente
El ejemplo proporcionado funciona, pero SelectMany no funciona exactamente como una unión. Una combinación permite "usar" cualquier campo de la tabla original más cualquier campo de la tabla unida. Pero aquí debe especificar un objeto de una lista adjunta a la tabla original. Por ejemplo, .SelectMany(c => new {c.CompanyName, c.Orders.ShippedDate});no funcionaría. SelectMany está más bien allanando la lista de listas, y puede elegir cualquiera (pero solo una a la vez) de las listas contenidas para el resultado. A modo de comparación: unirse internamente en Linq .
Matt
13

Seleccionar es una simple proyección uno a uno del elemento fuente a un elemento resultado. Select- Many se usa cuando hay múltiples cláusulas from en una expresión de consulta: cada elemento de la secuencia original se usa para generar una nueva secuencia.

Alexandr
fuente
7

Algunos SelectMany pueden no ser necesarios. Por debajo de 2 consultas dan el mismo resultado.

Customers.Where(c=>c.Name=="Tom").SelectMany(c=>c.Orders)

Orders.Where(o=>o.Customer.Name=="Tom")

Para una relación de 1 a muchos,

  1. si Start from "1", se necesita SelectMany, aplana los muchos.
  2. si Iniciar desde "Muchos", no se necesita SelectMany. ( todavía puede filtrar desde "1" , también esto es más simple que debajo de la consulta de unión estándar)

from o in Orders
join c in Customers on o.CustomerID equals c.ID
where c.Name == "Tom"
select o
Rm558
fuente
4

Sin ser demasiado técnico: base de datos con muchas organizaciones, cada una con muchos usuarios:

var orgId = "123456789";

var userList1 = db.Organizations
                   .Where(a => a.OrganizationId == orgId)
                   .SelectMany(a => a.Users)
                   .ToList();

var userList2 = db.Users
                   .Where(a => a.OrganizationId == orgId)
                   .ToList();

ambos devuelven la misma lista de ApplicationUser para la Organización seleccionada.

Los primeros "proyectos" de Organización a Usuarios, el segundo consulta la tabla Usuarios directamente.

RickL
fuente
3

Está más claro cuando la consulta devuelve una cadena (una matriz de caracteres):

Por ejemplo, si la lista 'Frutas' contiene 'manzana'

'Seleccionar' devuelve la cadena:

Fruits.Select(s=>s) 

[0]: "apple"

'SelectMany' aplana la cadena:

Fruits.SelectMany(s=>s)

[0]: 97  'a'
[1]: 112 'p'
[2]: 112 'p'
[3]: 108 'l'
[4]: 101 'e'
Eric Bole-Feysot
fuente
2

Solo para una vista alternativa que pueda ayudar a algunos programadores funcionales:

  • Select es map
  • SelectManyes bind(o flatMappara su gente Scala / Kotlin)
Matt Klein
fuente
2

Considere este ejemplo:

        var array = new string[2]
        {
            "I like what I like",
            "I like what you like"
        };
        //query1 returns two elements sth like this:
        //fisrt element would be array[5]  :[0] = "I" "like" "what" "I" "like"
        //second element would be array[5] :[1] = "I" "like" "what" "you" "like"
        IEnumerable<string[]> query1 = array.Select(s => s.Split(' ')).Distinct();

        //query2 return back flat result sth like this :
        // "I" "like" "what" "you"
        IEnumerable<string> query2 = array.SelectMany(s => s.Split(' ')).Distinct();

Entonces, como ve, los valores duplicados como "I" o "me gusta" se han eliminado de query2 porque "SelectMany" se aplana y proyecta en varias secuencias. Pero query1 devuelve la secuencia de matrices de cadenas. y dado que hay dos matrices diferentes en query1 (primer y segundo elemento), no se eliminaría nada.

MG Lee
fuente
probablemente mejor incluir ahora .Distinct () al final y decir que muestra "I" "como" "qué" "I" "como" "I" "como" "qué" "te gusta" "
Prof
1

Un ejemplo más de cómo se puede utilizar SelectMany + Select para acumular datos de objetos de submatriz.

Supongamos que tenemos usuarios con sus teléfonos:

class Phone { 
    public string BasePart = "555-xxx-xxx"; 
}

class User { 
    public string Name = "Xxxxx";
    public List<Phone> Phones; 
}

Ahora necesitamos seleccionar las BaseParts de todos los teléfonos de todos los usuarios:

var usersArray = new List<User>(); // array of arrays
List<string> allBaseParts = usersArray.SelectMany(ua => ua.Phones).Select(p => p.BasePart).ToList();
KEMBL
fuente
¿Cuál piensas que es mejor? Suyo ousersArray.SelectMany(ua => ua.Phones.Select(p => p.BasePart))
Michael Best
-1

Aquí hay un ejemplo de código con una pequeña colección inicializada para probar:

class Program
{
    static void Main(string[] args)
    {
        List<Order> orders = new List<Order>
        {
            new Order
            {
                OrderID = "orderID1",
                OrderLines = new List<OrderLine>
                {
                    new OrderLine
                    {
                        ProductSKU = "SKU1",
                        Quantity = 1
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU2",
                        Quantity = 2
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU3",
                        Quantity = 3
                    }
                }
            },
            new Order
            {
                OrderID = "orderID2",
                OrderLines = new List<OrderLine>
                {
                    new OrderLine
                    {
                        ProductSKU = "SKU4",
                        Quantity = 4
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU5",
                        Quantity = 5
                    }
                }
            }
        };

        //required result is the list of all SKUs in orders
        List<string> allSKUs = new List<string>();

        //With Select case 2 foreach loops are required
        var flattenedOrdersLinesSelectCase = orders.Select(o => o.OrderLines);
        foreach (var flattenedOrderLine in flattenedOrdersLinesSelectCase)
        {
            foreach (OrderLine orderLine in flattenedOrderLine)
            {
                allSKUs.Add(orderLine.ProductSKU);
            }
        }

        //With SelectMany case only one foreach loop is required
        allSKUs = new List<string>();
        var flattenedOrdersLinesSelectManyCase = orders.SelectMany(o => o.OrderLines);
        foreach (var flattenedOrderLine in flattenedOrdersLinesSelectManyCase)
        {
            allSKUs.Add(flattenedOrderLine.ProductSKU);
        }

       //If the required result is flattened list which has OrderID, ProductSKU and Quantity,
       //SelectMany with selector is very helpful to get the required result
       //and allows avoiding own For loops what according to my experience do code faster when
       // hundreds of thousands of data rows must be operated
        List<OrderLineForReport> ordersLinesForReport = (List<OrderLineForReport>)orders.SelectMany(o => o.OrderLines,
            (o, ol) => new OrderLineForReport
            {
                OrderID = o.OrderID,
                ProductSKU = ol.ProductSKU,
                Quantity = ol.Quantity
            }).ToList();
    }
}
class Order
{
    public string OrderID { get; set; }
    public List<OrderLine> OrderLines { get; set; }
}
class OrderLine
{
    public string ProductSKU { get; set; }
    public int Quantity { get; set; }
}
class OrderLineForReport
{
    public string OrderID { get; set; }
    public string ProductSKU { get; set; }
    public int Quantity { get; set; }
}
Sharunas Bielskis
fuente
-2

El SelectManymétodo derriba IEnumerable<IEnumerable<T>>a un IEnumerable<T>, como el comunismo, cada elemento se comporta de la misma manera (un tipo estúpido tiene los mismos derechos que uno genio).

var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }
snr
fuente
-5

Es la mejor manera de entender, creo.

            var query =
            Enumerable
                .Range(1, 10)
                .SelectMany(ints => Enumerable.Range(1, 10), (a, b) => $"{a} * {b} = {a * b}")
                .ToArray();

        Console.WriteLine(string.Join(Environment.NewLine, query));

        Console.Read();

Ejemplo de tabla de multiplicar.

usuario5966157
fuente
44
Solo si el significado de "mejor" ha cambiado dramáticamente.
Vahid Amiri
2
así que esta es la mejor forma en que piensas? Entonces, ¿cuál es la forma difícil de pensar?
Syed Ali el