Linq: SelectMany

Windows Phone From Scratch #47

A LINQ Tutorial

SelectMany is a crucial operator in both LINQ and in Reactive Extensions.  It may be SelectMany easier to see what it does by focusing on LINQ, as most of us are more familiar with querying against IEnumerables.

The primary job of SelectMany is to “flatten” a list of lists.

A practical example of this would occur if you had a collection of books, each of which had one or more authors and your goal was to obtain a list of all the authors of all the books.

Using Select would get you a collection of lists, each list containing one or more authors, but what you want is not a collection of lists but rather, a collection of Authors.

To see this at work, create a new Windows Phone application.

On the main page place a single list box,

<Grid
   x:Name="ContentPanel"
   Grid.Row="1"
   Margin="12,0,12,0">

   <ListBox
      Name="Output"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Stretch"
      Margin="10" />
</Grid>

We’ll need a very simple Book class,

public class Book
{
   public String Title { get; set; }
   public List<Author> Authors { get; set; }
   public int NumPages { get; set; }
}

Notice that the Authors property is a List<Author>.  We therefore need an Author class,

public class Author
{
   public string FirstName;
   public string LastName;
}

We can now generate a list of books. Each book will need a list of authors, a title and the number of pages in the book,

private void CreateBooks( )
{
   Books = new List<Book>( );
   Author auth1 = new Author( ) { FirstName = "Jesse", LastName = "Liberty" };
   Author auth2 = new Author( ) { FirstName = "Ian", LastName = "Griffiths" };
   Author auth3 = new Author( ) { FirstName = "Matthew", LastName = "Adams" };

   List<Author> authors = new List<Author>( ) { auth1, auth2, auth3 };
   Book newBook = new Book( ) { Authors = authors, NumPages = 500, Title = "Programming C#" };
   Books.Add( newBook );

   auth1 = new Author( ) { FirstName = "John", LastName = "Galloway" };
   authors = new List<Author>( ) { auth1 };
   newBook = new Book( ) { Authors = authors, NumPages = 350, Title = "Book 2" };
   Books.Add( newBook );

   auth1 = new Author( ) { FirstName = "Jesse", LastName = "Liberty" };
   auth2 = new Author( ) { FirstName = "Paul", LastName = "Betts" };
   authors = new List<Author>( ) { auth1, auth2 };
   newBook = new Book( ) { Authors = authors, NumPages = 375, Title = "Programming with Rx" };
   Books.Add( newBook );
}

With this data in place, representing data that might be retrieved from a database, we are ready to make our query, with a goal of getting a list of all the last names of all the authors of all the books.

We can do this with either Select or SelectMany. Select will return a collection of the authors for all the books, but that IEnumerable will be an IEnumerable of List of authors (since each book has a List of authors).  That’s fine, if a bit cumbersome:

 IEnumerable<List<Author>> EnumerableOfListOfAuthors = Books
    .Select( book => book.Authors );
 foreach ( var listOfAuthors in EnumerableOfListOfAuthors )
 {
    foreach ( Author auth in listOfAuthors )
    {
       Output.Items.Add( auth.LastName );
    }
 }

Because we have an enumerable of a list, we need an outer and an inner foreach loop so that we can get the last names of all the authors.

What we’d really like, though, instead of an Enumerable of a List, is just an Enumerable of Authors…. and that is what SelectMany is for,

IEnumerable<Author> authors = Books
   .SelectMany( book => book.Authors );
foreach ( Author auth in authors )
{
   Output.Items.Add( auth.LastName );
}

The output of both sets of code is identical (the list of all the authors) but the latter is far simpler and thus easier to understand and maintain.

Share

About Jesse Liberty

Jesse Liberty is an independent consultant and programmer with three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Xamarin Certified Mobile Developer and a Xamarin MVP, Microsoft MVP and Telerik MVP.
This entry was posted in Data, Linq, Mini-Tutorial, Patterns & Skills and tagged , . Bookmark the permalink.

7 Responses to Linq: SelectMany

  1. utini says:

    What is Select( book => book.Authors )
    what is book?
    what is => ?
    what is book.Authors?

    • Utni, you need to back up and read about lambda expressions.

      In short Select(book => book.authors) is the same as writing

      Select( function(Book book) { return book.Authors; });

  2. test says:

    This site definitely has all the information and facts I wanted concerningthis subject and didn’t know who to ask.

  3. Nick L says:

    I believe you can further shorten this like so:

    Output.AddRange(Books
    .SelectMany( book => book.Authors ) //List of Authors for a book
    .Select(author => author.LastName)); // List of Authors lastname for book

  4. Tore Aurstad says:

    Tested out your code here in Linqpad, interesting and well explained article about the SelectMany operator in Linq. It removes the need to do double for loops in code and simplifies extracting objects of given type which are lists inside lists

  5. Pingback: Windows Phone From Scratch – Linq: SelectMany | www.nalli.net

Leave a Reply

Your email address will not be published.