Différence entre Select et SelectMany

1074

J'ai cherché la différence entre Selectet SelectManymais je n'ai pas pu trouver de réponse appropriée. J'ai besoin d'apprendre la différence lors de l'utilisation de LINQ To SQL, mais tout ce que j'ai trouvé sont des exemples de tableaux standard.

Quelqu'un peut-il fournir un exemple LINQ To SQL?

Tarik
la source
8
vous pouvez regarder le code de SelectMany avec une fonction, ou avec deux fonctions referencesource.microsoft.com/#System.Core/System/Linq/…
barlop
1
Si vous connaissez Kotlin, il a des implémentations assez similaires pour les collections comme map aka C # Select et flatMap aka C # SelectMany. Fondamentalement, les fonctions d'extension de bibliothèque std de Kotlin pour les collections sont similaires à la bibliothèque C # Linq.
Arsenius

Réponses:

1622

SelectManyaplatit les requêtes qui renvoient des listes de listes. Par exemple

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

Démo en direct sur .NET Fiddle

Mike Two
la source
1
Question connexe sur l'imbrication de SelectMany pour aplatir une structure hiérarchique imbriquée.
The Red Pea
1
Pour comprendre resultSelector plus Le lien ci-dessous aide blogs.interknowlogy.com/2008/10/10/…
jamir
Une autre démonstration avec les résultats du parent: dotnetfiddle.net/flcdCC
Evgeniy Kosjakov
merci pour le lien violon!
Aerin
197

Sélectionner plusieurs, c'est comme une opération de jointure croisée dans SQL où il prend le produit croisé.
Par exemple, si nous avons

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

Sélectionnez plusieurs peuvent être utilisés pour obtenir l'ensemble suivant

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

Notez que nous prenons ici toutes les combinaisons possibles qui peuvent être faites à partir des éléments de l'ensemble A et de l'ensemble B.

Voici un exemple LINQ que vous pouvez essayer

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

le mélange aura les éléments suivants dans une structure plate comme

{(10,cat), (10,dog), (10,donkey), (20,cat), (20,dog), (20,donkey)}
Sriwantha Attanayake
la source
4
Je sais que c'est vieux, mais je voulais vous en remercier, ça m'a beaucoup sauvé! :) Il peut être utile d'avoir également une référence à ces codes: stackoverflow.com/questions/3479980/… Cheers!
user3439065
4
SelectMany ne doit pas être utilisé comme ça. Il a également la possibilité de prendre une seule fonction.
barlop
2
Je ne sais pas s'il est juste de dire que c'est ainsi SelectMany est . C'est plutôt un moyen qui SelectManypeut être utilisé, mais ce n'est pas vraiment la façon normale de l'utiliser.
Dave Cousineau
1
C'était la réponse la plus simple à comprendre pour moi.
Chaim Eliyah
Ce serait bien si vous démontrez également la Wherecondition après SelectMany
Nitin Kt
126

entrez la description de l'image ici

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
la source
9
grands exemples de données
ben_mj
2
pourriez-vous ajouter un exemple pour sélectionner pour compléter cette réponse :)
Harry
73

SelectMany()vous permet de réduire une séquence multidimensionnelle d'une manière qui nécessiterait autrement une seconde Select()ou une boucle.

Plus de détails sur ce billet de blog .

Michael Petrotta
la source
Mais le premier renvoie le type Enumerables d'enfants, le deuxième exemple renvoie le type de parents? En fait, je suis un peu confus, voudriez-vous l'ouvrir un peu plus?
Tarik
En fait, c'est l'inverse. La seconde aplatira complètement la hiérarchie des énumérables, afin que vous récupériez des enfants. Essayez l'article sur le lien que j'ai ajouté, voyez si cela aide.
Michael Petrotta
Le premier ne semble pas légal. Je pense que l'affiche s'est confondue. Le second retournerait un énumérable de parents.
mqp
Merci, eh bien en fait, les exemples étaient un peu déroutants :) mais merci encore d'avoir essayé de m'aider.
Tarik
37

Il y a plusieurs surcharges SelectMany. L'un d'eux vous permet de garder trace de toute relation entre le parent et les enfants tout en parcourant la hiérarchie.

Exemple : supposons que vous avez la structure suivante: League -> Teams -> Player.

Vous pouvez facilement retourner une collection plate de joueurs. Cependant, vous risquez de perdre toute référence à l'équipe dont le joueur fait partie.

Heureusement, il existe une surcharge à cette 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 
                                   };

L'exemple précédent est tiré du blog IK de Dan . Je vous recommande fortement d'y jeter un œil.

roland
la source
19

Je comprends que je SelectManytravaille comme un raccourci de jointure.

Afin que vous puissiez:

var orders = customers
             .Where(c => c.CustomerName == "Acme")
             .SelectMany(c => c.Orders);
Nathan Koop
la source
L'exemple fourni fonctionne, mais SelectMany ne fonctionne pas exactement comme une jointure. Une jointure permet "d'utiliser" n'importe quel champ de la table d'origine plus n'importe quel champ de la table jointe. Mais ici, vous devez spécifier un objet d'une liste attachée à la table d'origine. Par exemple, .SelectMany(c => new {c.CompanyName, c.Orders.ShippedDate});ne fonctionnerait pas. SelectMany aplatit plutôt la liste des listes - et vous pouvez choisir n'importe laquelle (mais une seule à la fois) des listes contenues pour le résultat. A titre de comparaison: jointure interne à Linq .
Matt
13

Select est une simple projection biunivoque de l'élément source vers un élément résultat. Select- Many est utilisé lorsqu'il existe plusieurs clauses from dans une expression de requête: chaque élément de la séquence d'origine est utilisé pour générer une nouvelle séquence.

Alexandr
la source
7

Certains SelectMany peuvent ne pas être nécessaires. En dessous de 2 requêtes donnent le même résultat.

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

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

Pour une relation 1 à plusieurs,

  1. si Start from "1", SelectMany est nécessaire, il aplatit le plus.
  2. si Start from "Many", SelectMany n'est pas nécessaire. ( toujours pouvoir filtrer à partir de "1" , c'est aussi plus simple que ci-dessous la requête de jointure standard)

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

Sans devenir trop technique - base de données avec de nombreuses organisations, chacune avec de nombreux utilisateurs: -

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

les deux renvoient la même liste ApplicationUser pour l'organisation sélectionnée.

Le premier "projets" de l'organisation aux utilisateurs, le second interroge directement la table des utilisateurs.

RickL
la source
3

Il est plus clair lorsque la requête renvoie une chaîne (un tableau de caractères):

Par exemple, si la liste «Fruits» contient «pomme»

'Select' renvoie la chaîne:

Fruits.Select(s=>s) 

[0]: "apple"

'SelectMany' aplatit la chaîne:

Fruits.SelectMany(s=>s)

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

Juste pour une autre vue qui peut aider certains programmeurs fonctionnels:

  • Select est map
  • SelectManyest bind(ou flatMappour votre peuple Scala / Kotlin)
Matt Klein
la source
2

Considérez cet exemple:

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

Ainsi, comme vous voyez des valeurs en double comme "I" ou "j'aime" ont été supprimées de query2 parce que "SelectMany" s'aplatit et se projette sur plusieurs séquences. Mais query1 renvoie une séquence de tableaux de chaînes. et comme il y a deux tableaux différents dans query1 (premier et deuxième élément), rien ne serait supprimé.

MG Lee
la source
il vaut probablement mieux inclure maintenant .Distinct () à la fin et indiquer qu'il affiche "I" "comme" "quoi" "I" "comme" "je" "comme" "ce que" "vous" "comme"
Prof
1

Un autre exemple de la façon dont SelectMany + Select peut être utilisé afin d'accumuler des données d'objets de sous-tableau.

Supposons que nous ayons des utilisateurs avec leurs téléphones:

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

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

Maintenant, nous devons sélectionner les BaseParts de tous les téléphones de tous les utilisateurs:

var usersArray = new List<User>(); // array of arrays
List<string> allBaseParts = usersArray.SelectMany(ua => ua.Phones).Select(p => p.BasePart).ToList();
KEMBL
la source
Selon vous, lequel est le meilleur? Le vôtre ouusersArray.SelectMany(ua => ua.Phones.Select(p => p.BasePart))
Michael Best
-1

Voici un exemple de code avec une petite collection initialisée pour les tests:

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

La SelectManyméthode fait tomber un IEnumerable<IEnumerable<T>>dans IEnumerable<T>, comme le communisme, chaque élément se comporte de la même manière (un gars stupide a les mêmes droits qu'un génie).

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

C'est la meilleure façon de comprendre je pense.

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

Exemple de table de multiplication.

user5966157
la source
4
Seulement si le sens de «meilleur» a changé de façon spectaculaire.
Vahid Amiri
2
c'est donc la meilleure façon de penser u ?? alors quelle est la manière difficile de penser ??
Syed Ali