22

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.

Ali Tarhini
  • 5,278
  • 6
  • 41
  • 66

6 Answers6

43

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.

Graham
  • 7,431
  • 18
  • 59
  • 84
Doctor Jones
  • 21,196
  • 13
  • 77
  • 99
17

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.

roland
  • 7,695
  • 6
  • 46
  • 61
  • "*One of them allows you to keep track of any relationship between parent and children*" actually two out of the [four methods](https://learn.microsoft.com/en-us/dotnet/api/system.linq.queryable.selectmany). And two of the four in addition give access to the index of the parent in its own collection. – mins May 14 '20 at 15:26
12

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.

Dean Chalk
  • 20,076
  • 6
  • 59
  • 90
4

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!

Tom
  • 3,354
  • 1
  • 22
  • 25
0

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
d219
  • 2,707
  • 5
  • 31
  • 36
Sam Saarian
  • 992
  • 10
  • 13
0

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.

Aussie Craig
  • 772
  • 4
  • 5