Different ways of using SelectMany()
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
精彩评论