Differenza tra Select e SelectMany


1074

Ho cercato la differenza tra Selecte SelectManyma non sono stato in grado di trovare una risposta adeguata. Devo imparare la differenza quando utilizzo LINQ To SQL ma tutto ciò che ho trovato sono esempi di array standard.

Qualcuno può fornire un esempio LINQ To SQL?


8
puoi vedere il codice di SelectMany con una funzione o con due funzioni referenceource.microsoft.com/#System.Core/System/Linq/…
barlop

1
Se hai familiarità con Kotlin ha implementazioni abbastanza simili per raccolte come map aka C # Select e flatMap aka C # SelectMany. Fondamentalmente le funzioni di estensione della libreria std di Kotlin per le raccolte hanno somiglianza con la libreria C # Linq.
Arsenius,

Risposte:


1622

SelectManyappiattisce le query che restituiscono elenchi di elenchi. Per esempio

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

Demo live su .NET Fiddle


1
Domanda correlata sull'annidamento di SelectMany per appiattire una struttura gerarchica nidificata.
The Red Pea,

1
Per capire il risultatoSelettore altro Il link seguente aiuta blogs.interknowlogy.com/2008/10/10/…
jamir

Un'altra demo con risultati del genitore: dotnetfiddle.net/flcdCC
Evgeniy Kosjakov

grazie per il link violino!
Aerin,

197

Selezionare molti è come un'operazione di cross join in SQL dove prende il prodotto incrociato.
Ad esempio se abbiamo

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

Seleziona molti possono essere utilizzati per ottenere il seguente set

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

Nota che qui prendiamo tutte le possibili combinazioni che possono essere fatte dagli elementi del set A e del set B.

Ecco un esempio LINQ che puoi provare

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

il mix avrà i seguenti elementi in struttura piatta come

{(10,cat), (10,dog), (10,donkey), (20,cat), (20,dog), (20,donkey)}

4
So che è vecchio, ma volevo ringraziarti per questo, mi ha salvato molto! :) Può essere utile avere anche un riferimento a quei codici: stackoverflow.com/questions/3479980/… Saluti!
user3439065

4
SelectMany non deve essere utilizzato in questo modo. Ha un'opzione per prendere anche solo una funzione.
barlop

2
Non so se è giusto dire che questo è come SelectMany è . Piuttosto, questo è un modo che SelectManypuò essere usato, ma in realtà non è il modo normale di usarlo.
Dave Cousineau,

1
Questa è stata la risposta più semplice per me da capire.
Chaim Eliyah,

Sarebbe bello se dimostrassi anche la Wherecondizione dopo SelectMany
Nitin Kt,

126

inserisci qui la descrizione dell'immagine

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

...


9
ottimi dati di esempio
ben_mj

2
potresti aggiungere un esempio per selezionare per completare questa risposta :)
Harry,

73

SelectMany()consente di comprimere una sequenza multidimensionale in un modo che richiederebbe altrimenti un secondo Select()o un ciclo.

Maggiori dettagli in questo post sul blog .


Ma il primo restituisce Enumerables type of Children il secondo esempio restituisce il tipo di Genitori? In realtà sono un po 'confuso, lo apriresti un po' di più?
Tarik,

Al contrario, in realtà. Il secondo appiattirà completamente la gerarchia degli enumerabili, in modo da riavere indietro Children. Prova l'articolo al link che ho aggiunto, vedi se aiuta.
Michael Petrotta,

Il primo non sembra essere legale. Penso che il poster si sia confuso. Il secondo restituirebbe un gran numero di genitori.
mqp

Grazie, in realtà sì, gli esempi sono stati un po 'confusi anche se :) Ma grazie ancora per aver cercato di aiutarmi.
Tarik,

37

Esistono diversi sovraccarichi SelectMany. Uno di questi ti consente di tenere traccia di qualsiasi relazione tra genitori e figli mentre attraversi la gerarchia.

Esempio : supponiamo di avere la seguente struttura:League -> Teams -> Player .

Puoi facilmente restituire una raccolta piatta di giocatori. Tuttavia, potresti perdere qualsiasi riferimento alla squadra di cui fa parte il giocatore.

Fortunatamente c'è un sovraccarico per tale scopo:

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

L'esempio precedente è tratto dal blog IK di Dan . Consiglio vivamente di dargli un'occhiata.


19

Capisco SelectMany di lavorare come una scorciatoia di join.

Così puoi:

var orders = customers
             .Where(c => c.CustomerName == "Acme")
             .SelectMany(c => c.Orders);

L'esempio fornito funziona, ma SelectMany non funziona esattamente come un join. Un join consente di "utilizzare" qualsiasi campo della tabella originale più qualsiasi campo della tabella unita. Ma qui devi specificare un oggetto di un elenco allegato alla tabella originale. Ad esempio, .SelectMany(c => new {c.CompanyName, c.Orders.ShippedDate});non funzionerebbe. SelectMany sta piuttosto appiattendo l'elenco degli elenchi - e puoi scegliere qualsiasi (ma solo uno alla volta) degli elenchi contenuti per il risultato. Per confronto: join interno in Linq .
Matt

13

Select è una semplice proiezione individuale dall'elemento sorgente a un elemento risultato. Select-Many viene utilizzato quando sono presenti più clausole from in un'espressione di query: ogni elemento nella sequenza originale viene utilizzato per generare una nuova sequenza.


7

Alcuni SelectMany potrebbero non essere necessari. Sotto 2 query danno lo stesso risultato.

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

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

Per una relazione 1 a molti,

  1. se Inizia da "1", SelectMany è necessario, appiattisce i molti.
  2. se Inizia da "Molti", SelectMany non è necessario. ( essere ancora in grado di filtrare da "1" , anche questo è più semplice della query di join standard sotto)

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

4

Senza diventare troppo tecnico - database con molte organizzazioni, ognuna con molti utenti: -

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

entrambi restituiscono lo stesso elenco ApplicationUser per l'organizzazione selezionata.

I primi "progetti" da Organizzazione a Utenti, il secondo interroga direttamente la tabella Users.


3

È più chiaro quando la query restituisce una stringa (una matrice di caratteri):

Ad esempio, se l'elenco "Frutta" contiene "mela"

'Seleziona' restituisce la stringa:

Fruits.Select(s=>s) 

[0]: "apple"

'SelectMany' appiattisce la stringa:

Fruits.SelectMany(s=>s)

[0]: 97  'a'
[1]: 112 'p'
[2]: 112 'p'
[3]: 108 'l'
[4]: 101 'e'

2

Solo per una vista alternativa che può aiutare alcuni programmatori funzionali là fuori:

  • Select è map
  • SelectManyè bind(o flatMapper il tuo popolo Scala / Kotlin)

2

Considera questo esempio:

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

Così come vedi valori duplicati come "I" o "like" sono stati rimossi da query2 perché "SelectMany" si appiattisce e proietta su più sequenze. Ma query1 restituisce una sequenza di array di stringhe. e poiché ci sono due matrici diverse in query1 (primo e secondo elemento), nulla verrebbe rimosso.


probabilmente meglio ora includere .Distinct () alla fine e dichiarare che produce "I" "like" "what" "I" "like" "I" "like" "what" "you" "like"
Prof

1

Un altro esempio di come SelectMany + Select può essere utilizzato per accumulare dati di oggetti dell'array secondario.

Supponiamo di avere utenti con i loro telefoni:

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

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

Ora dobbiamo selezionare BaseParts di tutti i telefoni di tutti gli utenti:

var usersArray = new List<User>(); // array of arrays
List<string> allBaseParts = usersArray.SelectMany(ua => ua.Phones).Select(p => p.BasePart).ToList();

Quale pensi sia meglio? Tuo ousersArray.SelectMany(ua => ua.Phones.Select(p => p.BasePart))
Michael Best,

-1

Ecco un esempio di codice con una piccola raccolta inizializzata per i test:

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

-2

Il SelectManymetodo abbatte un IEnumerable<IEnumerable<T>>in IEnumerable<T>, come il comunismo, ogni elemento si comporta allo stesso modo (uno stupido ha gli stessi diritti di un genio).

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

-5

Penso sia il modo migliore per capire.

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

Esempio di tabella di moltiplicazione.


4
Solo se il significato di "migliore" è cambiato radicalmente.
Vahid Amiri,

2
quindi questo è il modo migliore che pensi ?? allora qual è il modo di pensare difficile ??
Syed Ali,
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.