Projection Operators transforms the results of a query into a new form. The type in which these results are transformed are defined by the developers.
There are the following two types of Projection Operators:
- Select
- SelectMany
Let's understand these operators using an example.
1. Select Operator
If you want to select values from a single collection, then use the Select Operator.
Let's say we have a class Student with these five auto-implemented properties:
There is another class in the same cs file, in which there is a Main method from where the execution of every program starts.
We will create four separate objects of the Student class and we will add these objects to a list.
- class MainProgram {
- static void Main (string[] args) {
-
- Student studentOne = new Student {
- StudentId = 201,
- FirstName = "Sam",
- LastName = "Fisher",
- Gender ="Male",
- TotalMarks = 450
- };
-
-
- Student studentTwo = new Student {
- StudentId = 205,
- FirstName = "Max",
- LastName = "Payne",
- Gender ="Male",
- TotalMarks = 300.55
- };
-
-
- Student studentThree = new Student {
- StudentId = 210,
- FirstName = "Lara",
- LastName = "Croft",
- Gender ="Female",
- TotalMarks = 407.87
- };
-
-
- Student studentFour = new Student {
- StudentId = 250,
- FirstName = "Aiden",
- LastName = "Pearce",
- Gender ="Male",
- TotalMarks = 495
- };
Create a list collection of type Student and add these four Student objects to it.
Demo 1
Let's say we want to retrieve all the StudentIds from the StudentList.
We can say StudentList.Select(
Look at the first overloaded version of this Select operator parameter, it expects an object of type student. So, we can pass a lambda expression and based on the expression the result type will be decided and since we want to return a StudentId, so the TResult type will be an integer.
StudentList.Select(s => s.StudentId); //where s is a parameter of type student and s.StudentId is an expression
Look at the type, this Select function (Operator) is returning.
It returns an IEnumerable of integer but what will happen if I change the expression from s.StudentId to s.FirstName?
This time it returns an IEnumerable of string. So, based on the expression we pass, the result type TResult is assigned.
So, let's change it back to s.StudentId. Create a new IEnumerable<int> object and assign the result to it.
- IEnumerable<int> Ids = StudentList.Select(s => s.StudentId);
To retrieve all the Student Ids from this Ids object, we can use a foreach loop.
- foreach (var Id in Ids) {
- Console.WriteLine("Student Id {0}",String.Concat(Id));
- }
Run the application.
Demo 2
Let's say we want all the student ids but with the student ids we want their index position too.
For that we can use the second overloaded version of this Select function (operator) that expects two parameters. The first parameter is the type of list and here the type is Student and the second parameter is of type integer.
Look at the type of StudentId that we passed as a first parameter.
It states that StudentId is of type Student.
Look at the type that we passed as a second parameter.
It states i is of type integer.
So, we have passed the two required types of parameters and now it is time to pass an expression and the return type of the Select function will be based on the type of expression we pass in.
- StudentList.Select((StudentId, i) => new { Id = StudentId, Index = i });
This new { Id = StudentId, Index = i } is nothing but a new anonymous type of Student and integer. Using the projection operator the result query are transformed into a new form.
Now you might be wondering whether based on the expression we pass, the TResult type is assigned and now the result type is anonymous so in which type of object we can store the result.
If you hover your mouse over the Select function, you will see this function returns an IEnuemrable<a> object back.
Which means we can store the value in this type.
But when we try to create an object of that type, we don't get any suggestions.
And when we hard-code it, we get an error.
So, in which type of object can we assign the anonymous type?
Whenever a function returns an anonymous type back, we can create a var type object.
- var anonymous = StudentList.Select((StudentId, i) => new { Id = StudentId, Index = i });
If you hover your mouse on an anonymous var object, you will see this object is of type IEnumerable<a>. The type of this var object is derived based on the type of object we are assigning in it.
The next step is to retrieve the StudentId and Index and for that we can use a foreach loop.
- foreach (var item in anonymous) {
- Console.WriteLine("Student Id = {0}, Index Position = {1}", string.Concat(item.Id.StudentId), string.Concat(item.Index));
- }
The anonymous type Id is basically an anonymous student object and using that we will get all the properties.
Run the application.
Demo 3
Let's say we want to display the StudentId, FirstName and LastName concatenated as FullName, Gender and TotalMarks with the percentage of all the students.
Using the Student object stud we have created a new anonymous type objects.
Id, FullName, Gender, TotalMarks, Percentage.
The value for these anonymous types are coming from the stud Student object.
- Id = stud.StudentId => we are assigning the student id value in this new anonymous type.
- FullName = stud.FirstName + “ “ + stud.LastName => we are concatenating both the FirstName and LastName and assigning the result to the FullName that is a new anonymous type.
- Gender = stud.Gender => Just like StudentId we are assigning the Gender to the new anonymous type and the same goes for the TotalMarks TotalMarks = stud.TotalMarks.
Since these marks are of five subjects and to get the percentage we created a new anonymous type “Percentage” and assigned this expression stud.TotalMarks/5 + "%" that will provide us the percentage back and now all we need to do is loop using each anonymous type.
- foreach (var item in AllRecords) {
-
- Console.WriteLine("Student Id = {0},Student Name = {1},Gender = {2},Total Marks = {3},Percentage = {4}",item.Id,item.FullName,item.Gender,item.TotalMarks,item.Percentage);
- }
- #endregion
Run the application.
Summary
Just like the Select clause in SQL Server, using which we can select a specific column or multiple columns. In LINQ too we can do the same using the Select operator where we can select a specific property, multiple properties and in addition to that we can also do some calculations.
2. SelectMany
If you want to select values from a multiple collection, then use the SelectMany operator, meaning if you want the result from a list of lists and want to display as in a single sequence then the SelectMany operator can be very useful.
Let's say we have a class Student with five auto-implemented properties.
Out of these five properties, “Subject” is of type List<string> meaning it is a collection of strings.
Within the same cs file, I have another class MainProgram in which there is a Main method in which we will create four objects of this class.
- static void Main (string[] args) {
-
- Student studentOne = new Student {
- StudentId = 201,
- FirstName = "Sam",
- LastName = "Fisher",
- Gender ="Male",
- Subjects = new List<string>() { "C-Sharp", "ASP.NET", "XAML" }
- };
-
-
- Student studentTwo = new Student {
- StudentId = 205,
- FirstName = "Max",
- LastName = "Payne",
- Gender ="Male",
- Subjects = new List<string>() { "PHP", "Java" }
- };
-
-
- Student studentThree = new Student {
- StudentId = 210,
- FirstName = "Lara",
- LastName = "Croft",
- Gender ="Female",
- Subjects = new List<string>() { "PHP", "C-Sharp", "Phython", "Java" }
- };
-
-
- Student studentFour = new Student {
- StudentId = 250,
- FirstName = "Aiden",
- LastName = "Pearce",
- Gender ="Male",
- Subjects = new List<string>() { "C++" }
- };
- }
In the same main method, we will create a new list collection object of type student and add these four student objects.
Demo 1
Let's say we want to display all the subjects that students have opted for.
If you hover your mouse on s.Subjects you will get the intellisense as in the following:
It is a collection of List<string> and in our main method there are four student objects, meaning there will be four different IEnumerable of strings. But what this SelectMany is doing forming a single IEnumerable of string.
Run the application.
Demo 2
Let's see what happens if we try to do the same thing using a Select operator.
Look at the return type of the Select function, it is returning IEnumerable<List<string>> meaning a collection inside a collection.
To retrieve the subjects from this collection of collections, we can create a nested foreach loop.
The first loop will give us a List<string> and the second loop will give us a string back.
Run the application.
Demo 2
Let's say with the subjects we also want the FirstName and LastName to be displayed as FullName and with that we also want to display the Id and for that we can use the third overloaded version of this SelectMany function.
The first parameter expects an object of student and an expression of IEnumerable<TCollection>, in other words like a list of list collections.
The second parameter expects an object of type Student and an object of TCollection that is nothing but the type of collection we are dealing with and here the collection type is string.
The first parameter will give us the list of all subjects, in other words we will get four List objects.
The second parameter has two objects, one of type student that will give us the Id and Name of the students and another is of type string. The list of string that we got in the first parameter will be passed as a string value here that will give us the subjects and we are assigning it to a new anonymous type.
Now all we need to do is loop through each item present in the anonymous type.
Run the application.