Разница между 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.