Różnica między Select a SelectMany


1072

Szukałam różnicę między Selecta SelectMany, ale nie udało się znaleźć odpowiednią odpowiedź. Muszę nauczyć się różnicy podczas korzystania z LINQ To SQL, ale wszystko, co znalazłem, to standardowe przykłady tablic.

Czy ktoś może podać przykład LINQ To SQL?


8
możesz spojrzeć na kod SelectMany za pomocą jednej funkcji lub za pomocą dwóch funkcji referenceource.microsoft.com/#System.Core/System/Linq/...
barlop

1
Jeśli znasz Kotlin, ma on całkiem podobne implementacje dla kolekcji, jak mapa aka C # Select i flatMap aka C # SelectMany. Zasadniczo funkcje rozszerzenia biblioteki std Kotlin dla kolekcji mają podobieństwo do biblioteki C # Linq.
Arsenius

Odpowiedzi:


1618

SelectManyspłaszcza zapytania, które zwracają listy list. Na przykład

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

Wersja demonstracyjna na żywo .NET Fiddle


1
Podobne pytanie dotyczące zagnieżdżania SelectMany w celu spłaszczenia zagnieżdżonej struktury hierarchicznej.
The Red Pea

1
Aby zrozumieć wynikWybierz więcej Poniższy link pomaga blogs.interknowlogy.com/2008/10/10/…
jamir

Jeszcze jedno demo z wynikami od rodzica: dotnetfiddle.net/flcdCC
Evgeniy

dziękuję za link do skrzypce!
Aerin

197

Wybierz wiele jest jak operacja łączenia krzyżowego w SQL, gdzie bierze produkt krzyżowy.
Na przykład jeśli mamy

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

Wybierz wiele, aby uzyskać następujący zestaw

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

Zauważ, że tutaj bierzemy wszystkie możliwe kombinacje, które można wykonać z elementów zestawu A i zestawu B.

Oto przykład LINQ, którego możesz spróbować

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

mix będzie miał następujące elementy w płaskiej strukturze, takie jak

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

4
Wiem, że to stare, ale chciałem ci za to podziękować, bardzo mnie to uratowało! :) Przydatne może być także odniesienie do tych kodów: stackoverflow.com/questions/3479980/... Pozdrawiam!
user3439065

4
SelectMany nie musi być tak używane. Ma opcję, aby wziąć tylko jedną funkcję.
barlop

2
Nie wiem, czy słusznie jest powiedzieć, że tak SelectMany jest . Jest to raczej sposób, którego SelectManymożna użyć, ale tak naprawdę nie jest to normalny sposób korzystania z niego.
Dave Cousineau

1
To była najprostsza odpowiedź do zrozumienia.
Chaim Eliyah

Byłoby dobrze, gdybyś zademonstrował również Wherestan po SelectMany
Nitin Kt

126

wprowadź opis zdjęcia tutaj

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. Willa
  5. Busquets

...


9
świetny przykład danych
ben_mj

1
czy możesz dodać przykład dla select, aby wypełnić tę odpowiedź :)
Harry,

73

SelectMany()pozwala zwinąć wielowymiarową sekwencję w sposób, który wymagałby sekundy Select()lub pętli.

Więcej szczegółów w tym poście na blogu .


Ale pierwszy zwraca Zwracane typy Dzieci, drugi przykład zwraca Rodzice? Właściwie to jestem trochę zdezorientowany, czy otworzyłbyś to jeszcze bardziej?
Tarik

Właściwie na odwrót. Drugi całkowicie spłaszczy hierarchię wyliczeń, dzięki czemu odzyskasz Dzieci. Wypróbuj artykuł pod linkiem, który dodałem, i zobacz, czy to pomoże.
Michael Petrotta

Pierwszy nie wydaje się legalny. Myślę, że plakat sam się pomylił. Drugi zwróci pewną liczbę rodziców.
mqp

Dzięki, właściwie tak, przykłady były trochę mylące :) ale jeszcze raz dziękuję za próbę pomocy.
Tarik

37

Istnieje kilka przeciążeń do SelectMany. Jeden z nich pozwala śledzić wszelkie relacje między rodzicem a dziećmi podczas przechodzenia przez hierarchię.

Przykład : załóżmy, że mają następującą strukturę: League -> Teams -> Player.

Możesz łatwo zwrócić płaską kolekcję graczy. Możesz jednak stracić wszelkie odniesienia do drużyny, w której zawodnik jest członkiem.

Na szczęście w tym celu występuje przeciążenie:

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

Poprzedni przykład pochodzi z bloga IK Dana . Zdecydowanie polecam przyjrzeć się temu.


19

Rozumiem, SelectManyże działam jak skrót do łączenia.

Więc możesz:

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

Podany przykład działa, ale SelectMany nie działa dokładnie jak złączenie. Łączenie pozwala „używać” dowolnego pola oryginalnej tabeli oraz dowolnego pola połączonej tabeli. Ale tutaj musisz określić obiekt listy dołączony do oryginalnej tabeli. Na przykład .SelectMany(c => new {c.CompanyName, c.Orders.ShippedDate});nie działa. SelectMany raczej spłaszcza listę list - i możesz wybrać dowolną (ale tylko jedną) z zawartych list dla wyniku. Dla porównania: połączenie wewnętrzne w Linq .
Matt

13

Select to prosta projekcja jeden na jeden z elementu źródłowego na element wynikowy. Wybierz - Wiele jest używane, gdy w wyrażeniu zapytania występuje wiele klauzul: każdy element w oryginalnej sekwencji jest używany do generowania nowej sekwencji.


7

Niektóre SelectMany mogą nie być konieczne. Poniżej 2 zapytań daje ten sam wynik.

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

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

W przypadku relacji jeden do wielu

  1. jeśli Rozpocznij od „1”, SelectMany jest potrzebne, spłaszcza wiele.
  2. jeśli Rozpocznij od „Wiele”, SelectMany nie jest potrzebne. ( nadal można filtrować z „1” , jest to również prostsze niż poniżej standardowego zapytania o dołączenie)

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

4

Bez nadmiernej wiedzy technicznej - baza danych z wieloma organizacjami, każda z wieloma użytkownikami: -

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

oba zwracają tę samą listę ApplicationUser dla wybranej organizacji.

Pierwsze „projekty” od organizacji do użytkowników, drugie bezpośrednio sprawdzają tabelę użytkowników.


3

Jest bardziej jasne, gdy zapytanie zwraca ciąg znaków (tablicę znaków):

Na przykład jeśli lista „Owoce” zawiera „jabłko”

„Wybierz” zwraca ciąg znaków:

Fruits.Select(s=>s) 

[0]: "apple"

„SelectMany” spłaszcza ciąg:

Fruits.SelectMany(s=>s)

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

2

Tylko dla alternatywnego widoku, który może pomóc niektórym funkcjonalnym programistom:

  • Select jest map
  • SelectManyjest bind(lub flatMapdla twoich ludzi Scala / Kotlin)

2

Rozważ ten przykład:

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

Tak więc, jak widzisz, zduplikowane wartości, takie jak „I” lub „lubię to”, zostały usunięte z zapytania 2, ponieważ „SelectMany” spłaszcza i wyświetla w wielu sekwencjach. Ale zapytanie1 zwraca sekwencję tablic łańcuchowych. a ponieważ w zapytaniu 1 znajdują się dwie różne tablice (pierwszy i drugi element), nic nie zostanie usunięte.


prawdopodobnie lepiej teraz dołączyć .Distinct () na końcu i podać, że wypisuje „I” „like” „what” „I” „like” „I” „like” „what” „you” „like”
Prof

1

Jeszcze jeden przykład, w jaki sposób SelectMany + Select można wykorzystać do gromadzenia danych obiektów podtablicy.

Załóżmy, że mamy użytkowników z ich telefonami:

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

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

Teraz musimy wybrać BaseParts wszystkich użytkowników wszystkich użytkowników:

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

Który według ciebie jest lepszy? Pozdrawiam lubusersArray.SelectMany(ua => ua.Phones.Select(p => p.BasePart))
Michael Best

-1

Oto przykład kodu z zainicjowaną małą kolekcją do testowania:

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

Te SelectManymetody uderzenia w dół IEnumerable<IEnumerable<T>>w produkt IEnumerable<T>, podobnie jak komunizm, każdy element jest zachowywał się w taki sam sposób (głupi facet ma takie same prawa geniuszem jednym).

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

-5

Myślę, że to najlepszy sposób na zrozumienie.

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

Przykład tabliczki mnożenia.


4
Tylko jeśli znaczenie „najlepszego” zmieniło się diametralnie.
Vahid Amiri

2
więc to najlepszy sposób, w jaki myślisz? to jaki jest trudny sposób myślenia?
Syed Ali,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.