Updated on

Select() transforms each element into one new element. SelectMany() transforms each element into a sequence of elements, then concatenates those sequences into one flat result.

That difference shows up in the count. Ten departments through Select() give us ten results; ten departments through SelectMany() give us every employee in all of them. So the question to ask is never which method is better. It is whether the thing we are projecting to is one value or many.

To download the source code for this article, you can visit our GitHub repository.

Understanding the Select Method

When we talk about LINQ (Language Integrated Query) in C#, the Select() method is one of the first tools that comes to our minds.

We can use one of the available overloads:

public static IEnumerable<TResult> Select<TSource,TResult> (this IEnumerable<TSource> source, Func<TSource,TResult> selector);

This extension method accepts the transformation function with just a single parameter, which is the element of our collection. This is the method that most of us C# developers use daily.

Alternatively, we can use the other overload:

public static IEnumerable<TResult> Select<TSource,TResult> (this IEnumerable<TSource> source, Func<TSource,int,TResult> selector);

This time, the method accepts the transformation function with two parameters.

The second parameter represents the index of the collection element. This might be useful in cases where we would like to engage the element index in the transformation function.

Both methods are a fundamental part of LINQ, enabling us to perform projections on our data.

Simplifying Data Projection

In the context of LINQ, projection refers to the transformation of each collection element into a new form.

This is what the Select() method does. It takes each element in a collection and applies a function to it, resulting in a new collection where each element is the transformed version of the original.

To visualize this, let’s consider an example:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
var doubledNumbers = numbers.Select(num => num * 2);

Here, we use the lambda expression that describes how each element num in the numbers list should be transformed.

The result is a new doubledNumbers collection where each number is twice its original value.

Likewise, we can use the second overload:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
var transformedNumbers = numbers.Select((num, index) => num * index);

As we can see, like in the previous example we use the Select() method to transform a collection of integers, but this time we multiply each element of the collection by its index.

In this case, the result is also a new transformedNumbers collection.

Understanding the SelectMany Method

Now that we know how to use the Select() method in LINQ, let’s turn our attention to another powerful tool in the LINQ arsenal: the SelectMany() method.

Like in the previous case, LINQ provides us with a few overloaded methods to choose from:

public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult> (this IEnumerable<TSource> source, Func<TSource,IEnumerable<TCollection>> collectionSelector, Func<TSource,TCollection,TResult> resultSelector);

This extension method accepts the collectionSelector function that transforms the collection and the resultSelector function that transforms each element of the flattened collection.

Alternatively, we can use the other overload:

public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult> (this IEnumerable<TSource> source, Func<TSource,int,IEnumerable<TCollection>> collectionSelector, Func<TSource,TCollection,TResult> resultSelector);

Similar to the case of the Select() method, we can also use an overloaded method that engages the index of the element in the collection.

Next is the overload that we can use to flatten the collection without transforming its elements:

public static IEnumerable<TResult> SelectMany<TSource,TResult> (this IEnumerable<TSource> source, Func<TSource,IEnumerable<TResult>> selector);

This time, the extension method accepts only a selector function that is used to flatten the collection.

Lastly, we have a counterpart of that overload:

public static IEnumerable<TResult> SelectMany<TSource,TResult> (this IEnumerable<TSource> source, Func<TSource,int,IEnumerable<TResult>> selector);

In this overload, we also have a selector function accepted as a parameter, but we add a second parameter that represents the index of the element.

Flattening Collections

The SelectMany() method is designed for the complex scenarios where we deal with collections of collections.

It does more than transform each element in a collection; it flattens multiple nested collections into a single, one-dimensional sequence.

This flattening aspect is what sets the SelectMany() method apart from the Select() method.

To better understand the SelectMany() method let’s define a list:

var listOfDepartments = new List<Department>
{
    new Department
    {
        Name = "TechSupport",
        Employees = new()
        {
            new Employee { Name = "Thomas", JobPosition = JobPosition.Admin },
            new Employee { Name = "Cynthia", JobPosition = JobPosition.Admin }
        }
    },
    new Department
    {
        Name = "Development",
        Employees = new()
        {
            new Employee { Name = "Eric", JobPosition = JobPosition.Developer },
            new Employee { Name = "Laura", JobPosition = JobPosition.Developer },
            new Employee { Name = "Cedric", JobPosition = JobPosition.Developer }
        }
    },
    new Department
    {
        Name = "BackOffice",
        Employees = new()
        {
            new Employee { Name = "Monica", JobPosition = JobPosition.HumanResources }
        }
    }
};

Here, we have a list of instances of a Department class. The Department class contains a Name property representing the department name, and a list of Employee instances in a collection property named Employees.

Lastly, the Employee class contains Name and JobPosition properties that describe the employee.

Now, let’s see how we can use the SelectMany() method to flatten it:

var listOfEmployees = listOfDepartments.SelectMany(department => department.Employees);

Here, we use the SelectMany() method to extract Employee lists from each element of our listOfDepartments list and combine them into one list. After this operation, the listOfEmployees variable will contain a list of all of our employees.

Alternatively, we can use one of the overloads to enrich our employee details with department details:

var detailedListOfEmployees = listOfDepartments.SelectMany(department => department.Employees,
        (department, employee) => $"{employee.Name} | {department.Name} | {employee.JobPosition}")
    .ToList();

First, we are flattening our collection of Department objects to a list of Employee objects.

After that, we modify each resulting element of that operation by transforming it into a string containing details from both Department and Employee objects.

An operator is not the only way to get a flat sequence out of nested data. Our guide on flattening nested collections without LINQ covers the loop and recursion approaches for the cases where a query is not what we want.

What Is SelectMany in C# and What Does It Do?

Microsoft’s Enumerable.SelectMany reference defines it in one line: it “[p]rojects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence”. It is LINQ’s flattening operator.

Given a list of departments where each department holds a list of employees, Select() hands back a sequence of employee lists. SelectMany() hands back a sequence of employees.

It flattens exactly one level. A list of lists becomes a list; a list of lists of lists becomes a list of lists, and needs a second call.

Its second useful shape is the result-selector overload, which keeps the outer element in scope while projecting the inner one. That is how we get “employee plus the department they came from” in one pass, without a join.

The C# compiler leans on it too. Every extra from clause in a query expression becomes a SelectMany() call, which is why the method exists in the first place rather than as a convenience.

The types tell the whole story:

var employeeLists = listOfDepartments.Select(d => d.Employees);      // IEnumerable<List<Employee>>
var employees = listOfDepartments.SelectMany(d => d.Employees);      // IEnumerable<Employee>

Select vs SelectMany: What Is the Difference?

Now that we know both Select() and SelectMany() methods in LINQ, it’s important to understand how they differ and when to use each. While they may seem similar at first glance, they serve distinct purposes and are best put to use in different scenarios.

Both methods project. The difference is what the projection returns and what LINQ does with it afterwards.

Select() takes a selector that returns one value, and yields one result per source element. The source’s shape survives: ten in, ten out.

SelectMany() takes a selector that returns a sequence, and yields every element of every one of those sequences in order. Ten in gives us however many the inner sequences held between them, which may be more, fewer, or none.

Getting this wrong has a recognisable symptom. Projecting a collection property with Select() produces an IEnumerable<List<T>>, a sequence of lists, and the next line fails to compile, or a foreach hands us lists where we expected items.

Both defer execution, and both use the iterator pattern, so neither does any work until something enumerates the result.

Select()SelectMany()
Selector returnsOne value per elementA sequence per element
Result countSame as the sourceSum of the inner sequence lengths
Result shapeIEnumerable<TResult>IEnumerable<TResult>, flattened one level
Projecting a collection propertyIEnumerable<List<Employee>>, nestedIEnumerable<Employee>, flat
Levels flattenedNoneExactly one
Query-syntax equivalentA single select clauseA second from clause
Overload with the source elementNot applicable(source, inner) => ... result selector
Index overloadYesYes
ExecutionDeferredDeferred
Reach for it whenEach item maps to one itemEach item holds items we want individually

The compiler reaches for SelectMany() on our behalf as well. This query expression produces the same flat sequence as the method call:

var employees = from department in listOfDepartments
                from employee in department.Employees
                select employee;

That is not a coincidence of style. Microsoft’s LINQ reference records the rule: “In query expression syntax, each from clause (C#) or From clause (Visual Basic) after the initial one translates to an invocation of SelectMany.

Use Cases

First off, let’s consider when we should be using the Select() method and when the SelectMany() method.

The Select() method can be used to:

  • Convert a list of one type to another type
  • Get a specific property from objects in a collection
  • Perform calculations, like in our example, where we doubled each number

On the other hand, the SelectMany() method is particularly useful in scenarios where we need to:

  • Flatten nested collections or arrays
  • Perform operations that involve combining or merging data from multiple collections
  • Deal with hierarchical or relational data structures where elements are nested within other elements

As we can see, each of the methods has its unique purpose. Projection rarely travels alone, either: the Where() method narrows the source before we project it, and our article on selecting multiple records with LINQ applies the same projection to a list of keys.

Structure of Resultant Collection

Another key point to note is the difference in the structure of the resultant collections.

The Select() method preserves the original collection’s structure by default. If we start with a collection of 10 elements, we end up with another collection of 10 elements, albeit transformed.

In contrast, the SelectMany() method changes the structure of the collection. It combines elements from nested collections, often resulting in a collection larger than the original, depending on the nested elements.

Internal Logic of the Select Method

Now, let’s have a look at an interesting way of working behind the scenes of the Select() method:

Select projects each element to one result, preserving the element count

When we use the Select() method to project our collection into a new form, it doesn’t execute right away.

What it does is create an IEnumerable instance that holds all the details about the operation we want to perform. This is what we call deferred execution.

Now, when we start going through this IEnumerable, it steps in with the iterator pattern. This pattern lets it process each element one by one, using the yield return statement. Our article on how yield return powers deferred execution walks through the mechanics of that.

Each element goes through the transformation defined by a lambda expression that we give to the Select() method. This expression conveys what should be done with each element.

Internal Logic of the SelectMany Method

With all this in mind, now let’s have a look at what logic is applied to the SelectMany() method:

SelectMany projects each element to a sequence and concatenates them into one flat result

Just like its cousin the Select() method, the SelectMany() method embraces deferred execution and the iterator pattern.

As its iterator goes to work, it processes every element in the source collection, using a lambda expression we provide.

For every single item in our source collection, the SelectMany() method doesn’t stop at iterating over the IEnumerable provided by our lambda. It picks out each element in turn from these nested collections using yield return.

That is what gives us our single, flattened sequence.

When Should We Use SelectMany Instead of Select?

Use SelectMany() when each element holds several things we want to treat as individuals, and Select() when each element maps to exactly one thing.

The practical test is the type we want back. If the answer names a collection of collections, SelectMany() is the operator. If it names a collection of values, Select() is.

Reach for the result-selector overload when the flattened element alone is not enough, when the output needs to say which parent each item came from.

Two calls beat one when the nesting is two levels deep, but chaining them repeatedly is a sign the model is fighting us rather than the operator.

Neither method is a performance decision. Both are deferred and both stream, so the cost is the work in the lambda and the number of elements produced. SelectMany() produces more only because we asked for more.

Efficient Use of Select and SelectMany

Both Select() and SelectMany() methods can introduce overhead, especially when dealing with large collections.

Because of that, we should minimize the number of transformations and flattening operations. If possible, we should combine multiple transformations into a single Select() or SelectMany() call.

We need to remember that LINQ queries are not executed until we iterate over them. This can be both an advantage for efficiency and a source of confusion if not managed properly.

Because of that, we should plan when our data is queried and transformed.

While deferred execution can improve performance by not processing data until necessary, it can also lead to unexpected performance hits if the same query is iterated multiple times.

For that reason, we need to consider caching results if we want to reuse them.

Performance Considerations

Another thing to consider is the performance overhead of both methods.

The Select() method is generally efficient, but performance can degrade if the transformation function is complex or if it’s used on very large collections.

In general, the SelectMany() method can be more resource-intensive in comparison to the Select() method. Especially with large and deeply nested collections.

It can significantly increase the number of elements in the resulting collection, which may impact memory usage and processing time.

While it’s important to be aware of these performance considerations, we should avoid focusing too intently on performance optimization from the start as it can lead to complex, hard-to-read code.

To cope with that, we should always write clear, readable code first, then optimize based on profiling and actual performance needs.

Conclusion

In this article, we delved into LINQ’s Select and SelectMany methods in C#, exploring their distinct functionalities, use cases, and key differences.

While the Select method is ideal for simple transformations of collection elements, the SelectMany method excels in flattening nested collections, making it invaluable for more complex data structures.

By understanding the Select and SelectMany methods and adhering to best practices for efficiency and performance, we can write more efficient, readable, and maintainable C# code. LINQ itself keeps moving as well, and what changed in LINQ recently covers the operators that arrived with the newer releases.

Tested with .NET 10.0.10.