Разница между Select и SelectMany


1075

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

Может кто-нибудь привести пример LINQ To SQL?


8
Вы можете посмотреть на код для SelectMany с одной функцией или с двумя функциями Referenceource.microsoft.com/#System.Core/System/Linq/…
— barlop

1
Если вы знакомы с Kotlin, он имеет очень похожие реализации для коллекций, такие как карта, также называемая C # Select и flatMap, также называемая C # SelectMany. По сути, функции расширения библиотеки Kotlin std для коллекций похожи на библиотеку C # Linq.
— Арсений

Ответы:


1623

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/… Ура!
— user3439065

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. Alba
  3. ребро
  4. Вилла
  5. Бускетс

...


9
отличный пример данных
— ben_mj

2
Не могли бы вы добавить пример для выбора, чтобы завершить этот ответ :)
— Гарри

73

SelectMany() позволяет свернуть многомерную последовательность таким образом, что в противном случае потребовалась бы секунда Select() или цикл.

Более подробная информация в этом блоге .


Но первый из них возвращает тип Enumerables для детей, а второй пример возвращает тип для Parents? На самом деле, я немного смущен, не могли бы вы открыть его немного больше?
— Тарик

На самом деле, наоборот. Второй полностью сгладит иерархию перечислимых элементов, так что вы вернете детей. Попробуйте статью по ссылке, которую я добавил, посмотрите, поможет ли это.
— Майкл Петротта

Первый не представляется законным. Я думаю, что плакат запутался сам. Второй вернул бы множество родителей.
— 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 используется, когда в выражении запроса есть несколько предложений from: каждый элемент в исходной последовательности используется для генерации новой последовательности.


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):

Например, если список «Фрукты» содержит «яблоко»

«Выбрать» возвращает строку:

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'

3

Метод SelectMany () используется для выравнивания последовательности, в которой каждый из элементов последовательности является отдельным.

У меня userтакой же класс, как этот

class User
    {
        public string UserName { get; set; }
        public List<string> Roles { get; set; }
    }

главный:

var users = new List<User>
            {
                new User { UserName = "Reza" , Roles = new List<string>{"Superadmin" } },
                new User { UserName = "Amin" , Roles = new List<string>{"Guest","Reseption" } },
                new User { UserName = "Nima" , Roles = new List<string>{"Nurse","Guest" } },
            };

var query = users.SelectMany(user => user.Roles, (user, role) => new { user.UserName, role });

foreach (var obj in query)
{
    Console.WriteLine(obj);
}
//output

//{ UserName = Reza, role = Superadmin }
//{ UserName = Amin, role = Guest }
//{ UserName = Amin, role = Reseption }
//{ UserName = Nima, role = Nurse }
//{ UserName = Nima, role = Guest }
int[][] numbers = {
                new[] {1, 2, 3},
                new[] {4},
                new[] {5, 6 , 6 , 2 , 7, 8},
                new[] {12, 14}
            };

IEnumerable<int> result = numbers
                .SelectMany(array => array.Distinct())
                .OrderBy(x => x);

//output

//{ 1, 2 , 2 , 3, 4, 5, 6, 7, 8, 12, 14 }
 List<List<int>> numbers = new List<List<int>> {
                new List<int> {1, 2, 3},
                new List<int> {12},
                new List<int> {5, 6, 5, 7},
                new List<int> {10, 10, 10, 12}
            };

 IEnumerable<int> result = numbers
                .SelectMany(list => list)
                .Distinct()
                .OrderBy(x=>x);

//output

// { 1, 2, 3, 5, 6, 7, 10, 12 }

2

Просто для альтернативного представления, которое может помочь некоторым функциональным программистам:

  • Select является map
  • SelectManyесть bind(или flatMapдля ваших людей Scala / Kotlin)

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

Таким образом, как вы видите, повторяющиеся значения, такие как «I» или «like», были удалены из query2, потому что «SelectMany» выравнивает и проецирует через несколько последовательностей. Но query1 возвращает последовательность строковых массивов. и поскольку в query1 есть два разных массива (первый и второй элемент), ничего не будет удалено.


вероятно, лучше теперь включить .Distinct () в конце и заявить, что он выводит «I», «нравится», «что», «I», «нравится», «I», «нравится», «что», «вам», «нравится»
— проф.

1

Еще один пример того, как SelectMany + Select может использоваться для накопления данных объектов подмассива.

Предположим, у нас есть пользователи с их телефонами:

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

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

Теперь нам нужно выбрать базовые части всех телефонов всех пользователей:

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.