Windows Phone From Scratch #47
A LINQ Tutorial
SelectMany is a crucial operator in both LINQ and in Reactive Extensions. It may be 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.
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; });
excellent Source
This site definitely has all the information and facts I wanted concerningthis subject and didn’t know who to ask.
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
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