Різниця між Select і SelectMany


1072

Я шукав різницю між Selectі , SelectManyале я не зміг знайти найбільш прийнятних відповідей. Мені потрібно дізнатися різницю при використанні LINQ To SQL, але все, що я знайшов, - це стандартні приклади масиву.

Чи може хтось надати приклад LINQ до SQL?


8
ви можете подивитися код SelectMany з однією функцією або з двома функціями referenceource.microsoft.com/#System.Core/System/Linq/…
barlop

1
Якщо ви знайомі з Kotlin, він має досить схожі реалізації для колекцій, як map aka C # Select та flatMap aka C # SelectMany. В основному функції розширення бібліотеки Kotlin std для колекцій мають схожість з бібліотекою C # Linq.
Арсеній

Відповіді:


1618

SelectManyвирівнює запити, які повертають списки списків. Наприклад

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

Демонстрація демо на .NET Fiddle


1
Питання, пов’язані з розміщенням SelectMany для вирівнювання вкладеної ієрархічної структури.
Червоний горох

1
Щоб зрозуміти результатБільш докладніше Посилання нижче допомагає blogs.interknowlogy.com/2008/10/10/…
jamir

Ще одна демонстрація з результатами батьків: dotnetfiddle.net/flcdCC
Євгеній Косяков

дякую за скрипкове посилання!
Ерін

197

Вибрати багато - це як перехресне з'єднання в SQL, де воно бере продукт кросу.
Наприклад, якщо у нас є

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

Вибір багатьох може бути використаний для отримання наступного набору

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

Зауважимо, що тут ми беремо всі можливі комбінації, які можна скласти з елементів множини A і множини B.

Ось приклад LINQ, який ви можете спробувати

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

суміш матиме такі елементи, як плоска структура

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

4
Я знаю, що це по-старому, але я хотів подякувати вам за це, це мене дуже врятувало! :) Може бути корисним і посилання на ці коди: stackoverflow.com/questions/3479980/… Привіт!
користувач3439065

4
SelectMany не потрібно використовувати так. У нього є можливість просто взяти одну функцію.
барлоп

2
Я НЕ знаю , чи правильно це сказати , що це, як SelectMany це . Скоріше, це спосіб, який SelectManyможна використовувати, але насправді це не звичайний спосіб його використання.
Дейв Кузен

1
Це була найпростіша для мене відповідь.
Хаїм Елія

Було б добре, якщо ви також продемонструєте Whereстан після SelectMany
Nitin Kt

126

введіть тут опис зображення

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

foreach(var player in players)
{
    Console.WriteLine(player.LastName);
}
  1. Де Геа
  2. Альба
  3. Коста
  4. Вілла
  5. Букетки

...


9
чудовий приклад даних
ben_mj

1
ви можете додати приклад для вибору, щоб завершити цю відповідь :)
Гаррі

73

SelectMany()дозволяє згорнути багатовимірну послідовність способом, який інакше потребував би другого Select()або циклу.

Більше деталей у цій публікації в блозі .


Але перший повертає Перелічуючий тип дітей, другий, наприклад, тип повернення батьків? Насправді я трохи розгублений, ви б відкрили це трохи більше?
Тарік

Насправді, навпаки. Друга повністю вирівняє ієрархію перелічених чисел, щоб ви повернули Дітей назад. Спробуйте статтю за посиланням, яке я додав, подивіться, чи це допомагає.
Майкл Петротта

Перший не видається законним. Я думаю, що плакат заплутався сам. Друга повернула б чимало батьків.
mqp

Дякую, ну насправді, так, приклади ніби не збивали з пантелику Тхо :), але ще раз дякую за спробу допомогти мені.
Тарік

37

Існує кілька перевантажень SelectMany. Один з них дозволяє відслідковувати будь-які стосунки між батьком і дітьми під час проходження ієрархії.

Приклад : припустимо , що ви маєте таку структуру: League -> Teams -> Player.

Ви можете легко повернути рівну колекцію гравців. Однак ви можете втратити будь-яке посилання на команду, до складу якої входить гравець.

На щастя, для такої мети є перевантаження:

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

Попередній приклад взято з IK-блогу Дана . Настійно рекомендую поглянути на це.


19

Я розумію, SelectManyщо працює як ярлик приєднання.

Отже ви можете:

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

Наведений приклад працює, але SelectMany точно не працює як з'єднання. Об'єднання дозволяє "використовувати" будь-яке поле вихідної таблиці плюс будь-яке поле об'єднаної таблиці. Але тут потрібно вказати об’єкт списку, приєднаного до вихідної таблиці. Наприклад, .SelectMany(c => new {c.CompanyName, c.Orders.ShippedDate});не працювало б. SelectMany досить розгладжує список списків - і ви можете вибрати будь-який (але лише один за одним) із містяться списків для результату. Для порівняння: Внутрішнє приєднання до Linq .
Метт

13

Вибір - це проста проекція "один на один" від вихідного елемента до елемента результату. Select- Many використовується, коли в виразі запиту є кілька пунктів: кожен елемент у початковій послідовності використовується для створення нової послідовності.


7

Деякий SelectMany може не знадобитися. Нижче 2 запиту дають однаковий результат.

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

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

Для відносин 1 до багатьох,

  1. якщо Почати з "1", SelectMany потрібен, він згладжує багато.
  2. якщо Почати з "Багато", SelectMany не потрібен. ( все-таки зможете фільтрувати з "1" , також це простіше, ніж нижче стандартного запиту приєднання)

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

4

Без надто технічної - база даних з багатьма організаціями, кожна з якою має багато користувачів: -

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

обидва повертають один і той же список ApplicationUser для вибраної Організації.

Перший "проекти" від організації до користувачів, другий запитує безпосередньо таблицю користувачів.


3

Ясніше, коли запит повертає рядок (масив char):

Наприклад, якщо список "Фрукти" містить "яблуко"

'Select' повертає рядок:

Fruits.Select(s=>s) 

[0]: "apple"

'SelectMany' вирівнює рядок:

Fruits.SelectMany(s=>s)

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

2

Тільки для альтернативного перегляду, який може допомогти деяким функціональним програмістам там:

  • Select є map
  • SelectManyє bind(або flatMapдля ваших людей Скали / Котліна)

2

Розглянемо цей приклад:

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

Отже, як ви бачите, що повторювані значення типу "я" або "як" були видалені з запиту2, оскільки "SelectMany" вирівнюється і проектується в декількох послідовностях. Але query1 повертає послідовність масивів рядків. і оскільки у запиті1 (перший та другий елемент) є два різних масиви, нічого не буде видалено.


мабуть, краще зараз включити .Distinct () наприкінці і вказати, що вона виводить "I" "like" "what" "I" "like" "I" "like" "what" "you" "like"
Проф.

1

Ще один приклад того, як SelectMany + Select може бути використаний для накопичення даних об'єктів підмасиву.

Припустимо, у нас є користувачі, які мають телефони:

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

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

Тепер нам потрібно вибрати BaseParts усіх користувачів телефонів:

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

Що, на вашу думку, краще? Твій абоusersArray.SelectMany(ua => ua.Phones.Select(p => p.BasePart))
Майкл Кращий

-1

Ось приклад коду з ініціалізованою невеликою колекцією для тестування:

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

У SelectManyметоді збиває IEnumerable<IEnumerable<T>>в IEnumerable<T>, як комунізм, кожен елемент поводиться таким же чином (тупий хлопець має те ж права геніальні одного).

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

-5

Це найкращий спосіб зрозуміти, що я думаю.

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

Приклад таблиці множення.


4
Тільки якщо значення "найкращого" кардинально змінилося.
Вахід Амірі

2
так що це найкращий спосіб, коли ти думаєш ?? то який складний спосіб мислення ??
Сид Алі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.