开发者

Different ways of using SelectMany()

开发者 https://www.devze.com 2023-01-26 20:53 出处:网络
I开发者_如何学运维\'d like to know how to use SelectMany(). It seems to take so many arguments and from my own research I noticed that SelectMany() might be the \'father\' of all other select operatio

I开发者_如何学运维'd like to know how to use SelectMany(). It seems to take so many arguments and from my own research I noticed that SelectMany() might be the 'father' of all other select operations.


Select many allows you to select a property from your query source that is an IEnumerable<T> collection, but instead of returning a collection of collections (IEnumerable<IEnumerable<T>>) it will flatten the collections into a single collection.

Here's an example that you can run to demonstrate the differences between Select and 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);

By using SelectMany you make it easier to query values within child collections.


There are several overloads to SelectMany. One of them allows you to keep track of any relationship between parent and children while traversing the hierarchy.

Example: suppose you have the following structure: League -> Teams -> Player

You can easily return a flat collection of players. However you may loose any reference to the team a player is part of.

Fortunately there is an overload for such purpose:

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

The previous example is taken from Dan's IK blog:

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

I strongly recommend you take a look at it.


SelectMany basically flattens and processes hierarchical data, and has two main forms

(for the purposes of examples, see this initial code)

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

option 1) creates a collection from a collection of collections (essentially flattening hierarchical data)

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

The result is:

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

option 2) creates a collection from a collection of collections, and then processes each item of the new collection via a reference to the original parent

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

the result is:

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

each of the above useages has a variant, where the index of the item being processed is available to the transformation functions.


I use this extension all the time for diving into hierarchies.

Another cool way to do this when the Extensions get a bit messy is to use the formal LINQ way, like:

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

This would be the equivalent of:

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

This can get a bit long winded when adding in where clauses and joins etc. Hope this helps!


I have had some fun using SelectMany in LINQ. The following link described returning an IEnumerable in a LINQ select clause, which returns a sequence of sequences, and using SelectMany to flatten that into a simple sequence. "Linq to XML using Let, Yield return and Selectmany". It is not just a SelectMany use case, but part of an approach which generates multiple outputs from a single input in LINQ.


Here is another (VB.NET) usage example:

'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
0

精彩评论

暂无评论...
验证码 换一张
取 消