简体   繁体   中英

SelectMany nested in parent's lambda vs SelectMany after SelectMany

Those two examples give the same results, but different syntax tells me that are executed in a completely different way. Where is the difference? Which way should be preferred?

1st

continents
    .SelectMany(continent => continent.Countries)
    .SelectMany(country => country.Cities)

2nd

continents
    .SelectMany(continent => 
        continent.Countries.SelectMany(country => country.Cities))

EDIT: Let's not talk about deferred executions of IEnumerable, because it is not important here. Please assume that each query ends with .ToList().

Which way should be preferred? Blockquote

Any. Because you are working with IEnumerable methods that have deffered execution (learn more: MSDN )

You don't need a second SelectMany on your 2nd solution. In case you need to transform your IEnumerable into a List this would matter (a SelectMany taking more resources than a Select). As Alexbogs said in his answer if you only work with an IEnumerable it won't matter.

My proposal :

continents.SelectMany(continent => continent.Countries.Select(country => country.Cities))

I'm bemused by one of the other answers. Your queries both return a simple listing of cities and if that's all you need it doesn't really matter how you chain the SelectMany s. I think that's the only correct answer.

Replacing the second SelectMany by Select changes the query result significantly. It returns a nested listing of cities grouped by countries. So I'm not sure how that answers your question.

In other cases, it does matter how the parentheses are placed. In the first query the part continents.SelectMany(continent => continent.Countries) lists countries and after that, continents are out of scope. In the second query, continents can be kept in scope all the way.

The difference is best shown in query syntax. Suppose you want to list country and city names of all continents. In query syntax:

from continent in Continents
from country in continent.Countries
from city in country.Cities
select
new
{
    country.CountryName,
    city.CityName
}

In method syntax this amounts to:

Continents.SelectMany(continent => continent.Countries)
   .SelectMany(country => country.Cities,
        (country, city) => new 
        { 
            CountryName = country.CountryName, 
            CityName = city.CityName
        } )

As you see, it adds a Selectmany after the closing parenthesis of the first SelectMany as in your first query. Only countries can be kept in scope.

If you want to list continent names besides country names, and city names, you can use an overload of your second query, in method syntax:

Continents.SelectMany
(
    continent => continent.Countries.SelectMany
    (
        c => c.Cities
        , (country, city) => new { country, city }
    ), (continent, x) => new
    {
        continent.ContinentName,
        x.country.CountryName,
        x.city.CityName
    }
)

Again, query syntax looks a lot friendlier:

from continent in Continents
from country in continent.Countries
from city in country.Cities
select
new
{
    continent.ContinentName,
    country.CountryName,
    city.CityName
}

But the compiled method syntax is a bit different in transfering the intermediate anonymous types:

Continents.SelectMany(continent => continent.Countries,
      (continent, country) => new { continent = continent, country = country } )
   .SelectMany(x => x.country.Cities,(x, city) =>
         new
         {
             ContinentName = x.continent.ContinentName,
             CountryName = x.country.CountryName,
             CityName = city.CityName
         }
   )

Which is a variation of your first query.

So how to chain SelectMany s depends on which entities you need in the end result. The benefit of query syntax is that the compiler figures this out for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM