Projection in LINQ C# with the SelectMany operator

The SelectMany operator creates a one-to-many output projection sequence over an input sequence. SelectMany will return 0 or more output elements for every input element. Let’s see an example.

Data source:

string[] bands = { "ACDC", "Queen", "Aerosmith", "Iron Maiden", "Megadeth", "Metallica", "Cream", "Oasis", "Abba", "Blur", "Chic", "Eurythmics", "Genesis", "INXS", "Midnight Oil", "Kent", "Madness", "Manic Street Preachers"
, "Noir Desir", "The Offspring", "Pink Floyd", "Rammstein", "Red Hot Chili Peppers", "Tears for Fears"
, "Deep Purple", "KISS"};

Consider the following code:

IEnumerable<char> characters = bands.SelectMany(b => b.ToArray());
foreach (char item in characters)
{
	Console.WriteLine(item);
}

We provide a string input to SelectMany and apply the ToArray extension to it which will be an array of chars. So we have a single input – a string – and a collection of many elements as output – the sequence of characters from each band name.

Here’s a small part of the output:

SelectMany operator simple example

This is an interesting but probably not too useful application of SelectMany. We can do more interesting projections with it. Consider the following classes:

public class Singer
{
	public int Id { get; set; }
	public string FirstName { get; set; }
	public string LastName { get; set; }
}
public class Concert
{
	public int SingerId { get; set; }
	public int ConcertCount { get; set; }
	public int Year { get; set; }
}

We have the following data in the data collection:

public static IEnumerable<Singer> GetSingers()
{
	return new List<Singer>() 
	{
		new Singer(){Id = 1, FirstName = "Freddie", LastName = "Mercury"} 
		, new Singer(){Id = 2, FirstName = "Elvis", LastName = "Presley"}
		, new Singer(){Id = 3, FirstName = "Chuck", LastName = "Berry"}
		, new Singer(){Id = 4, FirstName = "Ray", LastName = "Charles"}
		, new Singer(){Id = 5, FirstName = "David", LastName = "Bowie"}
	};
}
public static IEnumerable<Concert> GetConcerts()
{
	return new List<Concert>()
	{
		new Concert(){SingerId = 1, ConcertCount = 53, Year = 1979}
		, new Concert(){SingerId = 1, ConcertCount = 74, Year = 1980}
		, new Concert(){SingerId = 1, ConcertCount = 38, Year = 1981}
		, new Concert(){SingerId = 2, ConcertCount = 43, Year = 1970}
		, new Concert(){SingerId = 2, ConcertCount = 64, Year = 1968}
		, new Concert(){SingerId = 3, ConcertCount = 32, Year = 1960}
		, new Concert(){SingerId = 3, ConcertCount = 51, Year = 1961}
		, new Concert(){SingerId = 3, ConcertCount = 95, Year = 1962}
		, new Concert(){SingerId = 4, ConcertCount = 42, Year = 1950}
		, new Concert(){SingerId = 4, ConcertCount = 12, Year = 1951}
		, new Concert(){SingerId = 5, ConcertCount = 53, Year = 1983}
	};
}

We can create a join with the SelectMany operator to see how many concerts each singer gave per year. Consider the following query:

IEnumerable<Singer> singers = GetSingers();
IEnumerable<Concert> concerts = GetConcerts();

var singerConcerts = singers.SelectMany(s => concerts.Where(c => c.SingerId == s.Id)
	.Select(c => new {Year = c.Year, ConcertCount = c.ConcertCount, Name = string.Concat(s.FirstName, " ", s.LastName) }));

foreach (var item in singerConcerts)
{
	Console.WriteLine(string.Concat(item.Name, ", ", item.Year, ", ", item.ConcertCount));
}

First every Singer object is passed into the lambda expression of SelectMany, which will be ‘s’ parameter. In the lambda expression we retrieve every Concert of each singer using the Where extension. We’re in effect joining the two data collections on the Id/SingerId properties. Finally we construct an anonymous object collection with the Select operator. Here’s the output:

SelectMany operator complex example

You can project the results into “real” objects as opposed to anonymous ones. We have the following object:

public class SingerConcert
{
	public string SingerName { get; set; }
	public int Year { get; set; }
	public int ConcertCount { get; set; }
}

Our query can be modified as follows to build a sequence of SingerConcert objects:

IEnumerable<Singer> singers = DemoCollections.GetSingers();
IEnumerable<Concert> concerts = DemoCollections.GetConcerts();

IEnumerable<SingerConcert> singerConcerts = singers.SelectMany(s => concerts.Where(c => c.SingerId == s.Id)
	.Select(c => new SingerConcert() { Year = c.Year, ConcertCount = c.ConcertCount, SingerName = string.Concat(s.FirstName, " ", s.LastName) }));

foreach (SingerConcert item in singerConcerts)
{
	Console.WriteLine(string.Concat(item.SingerName, ", ", item.Year, ", ", item.ConcertCount));
}

…which provides the same output as the anonymous class example above.

You can view all LINQ-related posts on this blog here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

One Response to Projection in LINQ C# with the SelectMany operator

  1. Shawn Joyce says:

    I love these excercises. Its really helping me practice and see how to build up my queries. In this example I struggled to understand the query itself till i found the DotNetPearls page on the join which suggested that join queries are probably easier to read and maintain in the comprehension syntax http://www.dotnetperls.com/join
    so i re-wrote my copy of the query:

    var query = from s in singers
    join c in concerts on s.Id equals c.SingerId
    select new { Year = c.Year, ConcertCount = c.ConcertCount, Name = string.Concat(s.FirstName, ” “, s.LastName) };

    foreach (var result in query)
    Console.WriteLine(“{0}, {1}, {2}”, result.Name, result.Year, result.ConcertCount);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: