Различные способы использования SelectMany()

Я хотел бы знать, как использовать SelectMany(). кажется, что так много аргументов, и из моего собственного исследования я заметил, что selectmany может быть "отцом" всех других операций выбора.

Ответы

Ответ 1

Выберите многие, чтобы выбрать свойство из источника запроса, который является IEnumerable <T> но вместо того, чтобы возвращать коллекцию наборов (IEnumerable < IEnumerable <T → ), она будет сглаживать коллекции в одну коллекцию.

Вот пример, который вы можете выполнить, чтобы продемонстрировать различия между Select и SelectMany:

//set up some data for our example
var tuple1 = new { Name = "Tuple1", Values = new int [] { 1, 2, 3 } };
var tuple2 = new { Name = "Tuple2", Values = new int [] { 4, 5, 6 } };
var tuple3 = new { Name = "Tuple3", Values = new int [] { 7, 8, 9 } };

//put the tuples into a collection
var tuples = new [] { tuple1, tuple2, tuple3 };

//"tupleValues" is an IEnumerable<IEnumerable<int>> that contains { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }
var tupleValues = tuples.Select(t => t.Values);

//"tupleSelectManyValues" is an IEnumerable<int> that contains { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
var tupleSelectManyValues = tuples.SelectMany(t => t.Values);

Используя SelectMany, вы упростите запрос значений в дочерних коллекциях.

Ответ 2

Есть несколько перегрузок до 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 
                                   };

Предыдущий пример взят из блога Dan IK:

http://blogs.interknowlogy.com/2008/10/10/use-linqs-selectmany-method-to-flatten-collections/

Я сильно рекомендую вам взглянуть на него.

Ответ 3

SelectMany в основном сглаживает и обрабатывает иерархические данные и имеет две основные формы.

(для примера см. этот исходный код)

class TestObj
{
    public string Name { get; set; }
    public List<string> Items { get; set; }
}

var hierarchicalCollection = new List<TestObj>();

hierarchicalCollection.Add(new TestObj() 
    {Items = new List<string>()
        {"testObj1-Item1", "testObj1-Item2"}, Name="t1"});
hierarchicalCollection.Add(new TestObj() 
    {Items = new List<string>()
        {"testObj2-Item1", "testObj2-Item2"}, Name="t2"});

вариант 1) создает коллекцию из коллекции коллекций (по существу сглаживая иерархические данные)

IEnumerable<string> flattenedCollection = 
    hierarchicalCollection.SelectMany(t => t.Items);

Результат:

"testObj1-Item1"
"testObj1-Item2"
"testObj2-Item1"
"testObj2-Item2"

вариант 2) создает коллекцию из коллекции коллекций, а затем обрабатывает каждый элемент новой коллекции с помощью ссылки на исходный родительский

IEnumerable<string> flattenedModifiedCollection = 
    hierarchicalCollection.SelectMany
        (t => t.Items, (t, i) => t.Name + " : " + i);

результат:

"t1 : testObj1-Item1"
"t1 : testObj1-Item2"
"t2 : testObj2-Item1"
"t2 : testObj2-Item2"

каждое из указанных выше вариантов использования имеет вариант, где индекс обрабатываемого элемента доступен для функций преобразования.

Ответ 4

Я использую это расширение все время для погружения в иерархии.

Еще один классный способ сделать это, когда Extensions становится немного беспорядочным, - использовать формальный способ LINQ, например:

var vehicles = from cust in context.Customers
               from fleet in cust.Fleets
               from v in fleet.Vehicles
               select v;

Это будет эквивалентно:

var vehicles = context.Customers.SelectMany(c => c.Fleets).SelectMany(f => f.Vehicles);

Это может быть немного длинным, когда вы добавляете туда предложения и соединения и т.д. Надеюсь, это поможет!

Ответ 5

Мне было весело с помощью SelectMany в LINQ. Следующая ссылка описала возвращение IEnumerable в предложение select LINQ, которое возвращает последовательность последовательностей и использование SelectMany, чтобы сгладить это в простую последовательность. "Linq to XML с использованием Let, Return Return и Selectmany" . Это не просто вариант использования SelectMany, а часть подхода, который генерирует несколько выходов с одного входа в LINQ.

Ответ 6

Вот еще один пример использования (VB.NET):

'Original list
Dim l() As String = {"/d", "/bc:\Temp\In*;c:\Temp\Out", "/hABC", "/s123"}

'Processed list: will list first 2 characters from each string member.
Dim L1 As IEnumerable(Of String) = l.SelectMany(Function(x As String) {x.Substring(0, 2)})

Dim L2 As List(Of String) = l.SelectMany(Function(x As String) {x.Substring(0, 2)}).ToList

'Will return dictionary like list with keys==2 characters and values the rest from each string member.
Dim L3 As List(Of KeyValuePair(Of String, String)) = l.SelectMany(Function(x As String) {New KeyValuePair(Of String, String)(x.Substring(0, 2), x.Substring(2))}).ToList