Wednesday, 19 August 2015

Difference between Select and SelectMany in LINQ

In the below example we shall see what exactly is the difference while accessing the properties.
When we access some data by using select it gives us the data that is grouped under the parent (i.e gives multiple arrays). To access the output we need to loop twice.
////This code shows how to use Select()             
var Query2 = Builders.Select(Build => Build.Locations);

// Notice that two foreach loops are required to iterate through
// the results because the query returns a collection of arrays.            
foreach (List<String> BuildList in Query2)
{
        foreach (string builds in BuildList)
        {
                Console.WriteLine(builds);
        }

        Console.WriteLine();
}
But using the same sample when we use select many it gives the selected items in one array(i.e it give the output as it is joining all the output from select command)
// Query using SelectMany().            
var query1 = Builders.SelectMany(Build => Build.Locations);

// Only one foreach loop is required to iterate through the
// results because it is a one-dimensional collection.
// Select many joins the output of all the sub collections in to one.            
foreach (string Loc in query1)
        Console.WriteLine(Loc);
Output:
Difference select and selectMany
Difference Select 

1.gif




My collection of Leagues, Teams, and Players has a structure like the following:
  • League: AFC-West
    • Team: Chargers
      • Player: Rivers
      • Player: Tomlinson
      • Player: Gates
    • Team: Broncos
      • Player: Cutler
      • Player: Bailey
      • Player: Marshall
  • League: AFC-South
    • Team: Colts
      • Player: Manning
      • Player: Addai
      • Player: Vinatieri
Now let’s write some queries (I use LINQPad to play with these queries and get immediate feedback/output).
This most basic version gives you a flat collection of IEnumerable<Team> of all the teams in across all leagues.
var allTeams = from t in leagues.
    SelectMany( l => l.Teams )
    select t;
You can chain the SelectMany calls together to dive as deep as you want in the hierarchy.  This returns an IEnumerable<Player> of all players in all leagues & teams.
var allPlayers = from p in leagues
    .SelectMany( l => l.Teams )
    .SelectMany( t => t.Players )
    select p;
And of course you can add where clause criteria to further refine which players you get now that you’re operating on the flat list of players.
var onlyYoungPlayers = from p in leagues
    .SelectMany( l => l.Teams )
    .SelectMany( t => t.Players )
    where p.Age < 30
    select p;
Here’s a good way to chain Where( ) in between the SelectMany( ) calls get all the players only from certain teams.
var players = leagues.SelectMany( l => l.Teams )
    .Where( t => t.NumberOfWins > 2  )
    .SelectMany( t => t.Players )
    .Where( p => p.HomeState == "CA" )
    .Select( p => p );
The 3rd and 4th overloads of the SelectMany( ) method with the additional “result selector” parameter take a bit more explanation.  I look this extra result selector parameter as a helper object to help you know the relationship between the parent and child collections.  Say you need a result collection that will not only have the full list of Teams that match some criteria in all leagues, but need to know what League the teams are in.  If you use one of the above queries, you get the flat list of Team objects, but have lost their connection to what League they’re from.  (Sometimes you have “back references” in your object model to get from a child object to their parent, which makes using this result selector unnecessary, but you don’t always have that benefit.)  The result selector is an intermediate object availble within the scope of the query to give you the information you need, and it’s up to you to decide what data you need in the result selector to help you.  Here’s an example:
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 };
So the helper object is created from the 2-argument Func<> which takes the object from the parent collection and the object from the child collection that are getting paired up during the query processing, and you can do whatever you want with them – I’m just doing something easy that creates a new anonymous type with those objects embedded in them, which gives me the ability to use that data in the where clause, as well as in the results of the query.  (Note:  you can just “select” the whole helper object in the final line of the query as well)

No comments:

Post a Comment