Projection Operations

Projection operations transform objects into new forms typically consisting of only properties subsequently used.

It constructs a new type from each object. A property can be projected prior to performing a mathematical function on it. Projection can occur with no change to the original object.

The query operator methods used to perform projection follow: Select and SelectMany. The Select method projects values based on a transform function. It uses the following syntax:

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

Review an example below:

List<string> terms = new List<string>() { "your", "purple", "fox",
"all", "day", "all", "night" };
var query = from term in terms
			select term.Substring(0, 1);
foreach (string q in query)
	Console.WriteLine(q);

The SelectMany method projects value sequences based on a transform function which it then forms into a single sequence. It uses the following syntax:

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

Review an example below:

List<string> lyrics = new List<string>() { "all the critics love you in New York",
"all I wanna do is Dance" };
var query = from lyric in lyrics
			from term in lyric.Split(' ')
			select term;
foreach (string q in query)
	Console.WriteLine(q);

SEPARATING FUNCTION

Both Select and SelectMany yield a result value(s) from source values, however, Select produces a single result for each source value. This results in a collection of elements with the same quantity of elements as its source collection. Compare this to SelectMany yielding one result containing concatenated sub-collections from each source value.

FORMS

Use the select clause to perform a wide variety tasks. Review some of the many ways to use select below:

class SelectExample
{
	// class definitions
	public class Player
	{
		public string GivenNm { get; set; }
		public string SurNm { get; set; }
		public int ID { get; set; }
		public List<int> Rankings;
		public ContactInfo GetContactInfo(SelectExample app, int id)
		{
			ContactInfo cInfo =
				(from ci in app.contactList
				where ci.ID == id
				select ci)
				.FirstOrDefault();
				return cInfo;
		}
		public override string ToString()
		{
			return GivenNm + " " + SurNm + ":" + ID;
		}
	}
	public class ContactInfo
	{
		public int ID { get; set; }
		public string Email { get; set; }
		public string Mobile { get; set; }
		public override string ToString() { return Email + "," + Mobile; }
	}
	public class RankInfo
	{
	public double Average { get; set; }
	public int ID { get; set; }
	}
						
	// primary data source
	List<Player> players = new List<Player>()
	{
		new Player {GivenNm="Mike", SurNm="Smith", ID=111,
		Rankings= new List<int>() {110, 132, 145, 99}},
		new Player {GivenNm="Stacy", SurNm="Thompson", ID=112,
		Rankings= new List<int>() {55, 63, 45, 77}},
		new Player {GivenNm="Bernard", SurNm="Wright", ID=113,
		Rankings= new List<int>() {71, 84, 75, 99}},
		new Player {GivenNm="Jermaine", SurNm="Jackson", ID=114,
		Rankings= new List<int>() {70, 88, 89, 90}},
	};

	// contact info data source
	List<ContactInfo> contactList = new List<ContactInfo>()
	{
		new ContactInfo {ID=111, Email="msmitty@Contoso.com", Mobile="555-505-0101"},
		new ContactInfo {ID=112, Email="stacyt@Contoso.com", Mobile="555-505-0248"},
		new ContactInfo {ID=113, Email="thenard@Contoso.com", Mobile="555-505-1440"},
		new ContactInfo {ID=114, Email="jermj@Contoso.com", Mobile="555-505-0231"}
	};
						
	static void Main(string[] args)
	{
	SelectExample app = new SelectExample();
						
	// make a filtered sequence of unmodified Players
	IEnumerable<Player> playerQueryOne =
		from player in app.players
		where player.ID > 111
		select player;
	Console.WriteLine("First Query: select range_variable");
	foreach (Player p in playerQueryOne)
	{
		Console.WriteLine(p.ToString());
	}

	// Make a filtered sequence of elements containing
	// only one property of each Player.
	IEnumerable<String> playerQueryTwo =
		from player in app.players
		where player.ID > 111
		select player.SurNm;
	Console.WriteLine("\r\n playerQueryTwo: select range_variable.Property");
	foreach (string p in playerQueryTwo)
	{
		Console.WriteLine(p);
	}
						
	// Make a filtered sequence of objects produced by
	// a method call on each Player.
	IEnumerable<ContactInfo> playerQueryThree =
		from player in app.players
		where player.ID > 111
		select player.GetContactInfo(app, player.ID);
						
	Console.WriteLine("\r\n playerQueryThree: select range_variable.Method");
	foreach (ContactInfo ci in playerQueryThree)
	{
		Console.WriteLine(ci.ToString());
	}
						
	// Make a filtered sequence of ints from
	// the internal array inside each Player.
	IEnumerable<int> playerQueryFour =
		from player in app.players
		where player.ID > 111
		select player.Rankings[0];
						
	Console.WriteLine("\r\n playerQueryFour: select range_variable[index]");
	foreach (int i in playerQueryFour)
	{
		Console.WriteLine("1st rank = {0}", i);
	}
						
	// Make a filtered sequence of doubles;
	// the expression result.
	IEnumerable<double> playerQueryFive =
		from player in app.players
		where player.ID > 111
		select player.Rankings[0] * 1.1;
	Console.WriteLine("\r\n playerQueryFive: select expression");
	foreach (double d in playerQueryFive)
	{
		Console.WriteLine("Adjusted 1st ranking = {0}", d);
	}
						
	// Produce a filtered sequence of doubles that are
	// the result of a method call.
	IEnumerable<double> playerQuerySix =
		from player in app.players
		where player.ID > 111
		select player.Rankings.Average();
						
	Console.WriteLine("\r\n playerQuerySix: select expression2");
	foreach (double d in playerQuerySix)
	{
		Console.WriteLine("Average = {0}", d);
	}
						
	// Make a filtered sequence of anonymous types
	// containing only two properties from each Player.
	var playerQuerySeven =
		from player in app.players
		where player.ID > 111
		select new { player.GivenNm, player.SurNm };
						
	Console.WriteLine("\r\n playerQuerySeven: select new anonymous type");
	foreach (var item in playerQuerySeven)
	{
		Console.WriteLine("{0}, {1}", item.SurNm, item.GivenNm);
	}
	// Make a filtered sequence of named objects containing
	// a method return value and a property from each Player.
	IEnumerable<RankInfo> playerQueryEight =
		from player in app.players
		where player.ID > 111
		select new RankInfo
		{
			Average = player.Rankings.Average(),
			ID = player.ID
		};
						
	Console.WriteLine("\r\n playerQueryEight: select new named type");
	foreach (RankInfo ri in playerQueryEight)
	{
		Console.WriteLine("ID = {0}, Average = {1}", ri.ID, ri.Average);
	}

	// Make a filtered sequence of players from a contact list
	// and who average over 60.
	IEnumerable<ContactInfo> playerQueryNine =
		from player in app.players
		where player.Rankings.Average() > 60
		join ci in app.contactList on player.ID equals ci.ID
		select ci;

	Console.WriteLine("\r\n playerQueryNine: select result of join clause");
	foreach (ContactInfo ci in playerQueryNine)
	{
		Console.WriteLine("ID = {0}, Email = {1}", ci.ID, ci.Email);
	}
	Console.WriteLine("Press a key to continue.");
	Console.ReadKey();
	}
}