标签:set next object pes method make 传递 format ict
In this section, you will learn some complex LINQ queries. We will use the following Student and Standard collection for our queries.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18, StandardID = 1 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 21, StandardID = 1 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18, StandardID = 2 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20, StandardID = 2 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 21 }
};
IList<Standard> standardList = new List<Standard>() {
new Standard(){ StandardID = 1, StandardName="Standard 1"},
new Standard(){ StandardID = 2, StandardName="Standard 2"},
new Standard(){ StandardID = 3, StandardName="Standard 3"}
};
var studentNames = studentList.Where(s => s.Age > 18)
.Select(s => s)
.Where(st => st.StandardID > 0)
.Select(s => s.StudentName);
Output:
Steve
Ram
The following query returns list students group by StandardID:
var studentsGroupByStandard = from s in studentList
group s by s.StandardID into sg
orderby sg.Key
select new { sg.Key, sg };
foreach (var group in studentsGroupByStandard)
{
Console.WriteLine("StandardID {0}:", group.Key);
group.sg.ToList().ForEach(st => Console.WriteLine(st.StudentName ));
}
StandardID 0:
Ron
StandardID 1:
John
Steve
StandardID 2:
Bill
Ram
The output includes Ron who doesn‘t have any StandardID. So Ron falls under StandardID 0.
To remove a student who doesn‘t have a StandardID, use a where operator before the group operator:
var studentsGroupByStandard = from s in studentList
where s.StandardID > 0
group s by s.StandardID into sg
orderby sg.Key
select new { sg.Key, sg };
StandardID 1:
John
Steve
StandardID 2:
Bill
Ram
Use left outer join to display students under each standard. Display the standard name even if there is no student assigned to that standard.
var studentsGroup = from stad in standardList
join s in studentList
on stad.StandardID equals s.StandardID
into sg
select new {
StandardName = stad.StandardName,
Students = sg
};
foreach (var group in studentsGroup)
{
Console.WriteLine(group.StandardName);
group.Students.ToList().ForEach(st => Console.WriteLine(st.StudentName));
}
Standard 1:
John
Steve
Standard 2:
Bill
Ram
Standard 3:
In the following example of group by query, we sort the group and select only StudentName:
var studentsWithStandard = from stad in standardList
join s in studentList
on stad.StandardID equals s.StandardID
into sg
from std_grp in sg
orderby stad.StandardName, std_grp.StudentName
select new {
StudentName = std_grp.StudentName,
StandardName = stad.StandardName
};
foreach (var group in studentsWithStandard)
{
Console.WriteLine("{0} is in {1}", group.StudentName, group.StandardName);
}
John is in Standard 1
Steve is in Standard 1
Bill is in Standard 2
Ram is in Standard 2
The following query returns list of students by ascending order of StandardID and Age.
var sortedStudents = from s in studentList
orderby s.StandardID, s.age
select new {
StudentName = s.StudentName,
Age = s.age,
StandardID = s.StandardID };
sortedStudents.ToList().ForEach(s => Console.WriteLine("Student Name: {0}, Age: {1}, StandardID: {2}", s.StudentName, s.Age , s.StandardID));
Student Name: Ron, Age: 21, StandardID: 0
Student Name: John, Age: 18, StandardID: 1
Student Name: Steve, Age: 21, StandardID: 1
Student Name: Bill, Age: 18, StandardID: 2
Student Name: Ram, Age: 20, StandardID: 2
var studentWithStandard = from s in studentList
join stad in standardList
on s.StandardID equals stad.StandardID
select new {
StudentName = s.StudentName,
StandardName = stad.StandardName
};
studentWithStandard.ToList().ForEach(s => Console.WriteLine("{0} is in {1}", s.StudentName, s.StandardName ));
John is in Standard 1
Steve is in Standard 1
Bill is in Standard 2
Ram is in Standard 2
var nestedQueries = from s in studentList
where s.age > 18 && s.StandardID ==
(from std in standardList
where std.StandardName == "Standard 1"
select std.StandardID).FirstOrDefault()
select s;
nestedQueries.ToList().ForEach(s => Console.WriteLine(s.StudentName));
Steve
The joining operators joins the two sequences (collections) and produce a result.
Joining Operators | Usage |
---|---|
Join | The Join operator joins two sequences (collections) based on a key and returns a resulted sequence. |
GroupJoin | The GroupJoin operator joins two sequences based on keys and returns groups of sequences. It is like Left Outer Join of SQL. |
The Join operator operates on two collections, inner collection & outer collection. It returns a new collection that contains elements from both the collections which satisfies specified expression. It is the same as inner join of SQL.
The Join extension method has two overloads as shown below.
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer,
IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector);
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector,
IEqualityComparer<TKey> comparer);
As you can see in the first overload method takes five input parameters (except the first ‘this‘ parameter): 1) outer 2) inner 3) outerKeySelector 4) innerKeySelector 5) resultSelector.
Let‘s take a simple example. The following example joins two string collection and return new collection that includes matching strings in both the collection.
IList<string> strList1 = new List<string>() {
"One",
"Two",
"Three",
"Four"
};
IList<string> strList2 = new List<string>() {
"One",
"Two",
"Five",
"Six"
};
var innerJoin = strList1.Join(strList2,
str1 => str1,
str2 => str2,
(str1, str2) => str1);
One
Two
Now, let‘s understand join metohod using following Student and Standard class where Student class includes StandardID that matches with StandardID of Standard class.
public class Student{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int StandardID { get; set; }
}
public class Standard{
public int StandardID { get; set; }
public string StandardName { get; set; }
}
The following example demonstrates LINQ Join query.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", StandardID =1 },
new Student() { StudentID = 2, StudentName = "Moin", StandardID =1 },
new Student() { StudentID = 3, StudentName = "Bill", StandardID =2 },
new Student() { StudentID = 4, StudentName = "Ram" , StandardID =2 },
new Student() { StudentID = 5, StudentName = "Ron" }
};
IList<Standard> standardList = new List<Standard>() {
new Standard(){ StandardID = 1, StandardName="Standard 1"},
new Standard(){ StandardID = 2, StandardName="Standard 2"},
new Standard(){ StandardID = 3, StandardName="Standard 3"}
};
var innerJoin = studentList.Join(// outer sequence
standardList, // inner sequence
student => student.StandardID, // outerKeySelector
standard => standard.StandardID, // innerKeySelector
(student, standard) => new // result selector
{
StudentName = student.StudentName,
StandardName = standard.StandardName
});
The following image illustrate the parts of Join operator in the above example.
In the above example of join query, studentList is outer sequence because query starts from it. First parameter in Join method is used to specify the inner sequence which is standardList in the above example. Second and third parameter of Join method is used to specify a field whose value should be match using lambda expression in order to include element in the result. The key selector for the outer sequence student => student.StandardID
indicates that take StandardID field of each elements of studentList should be match with the key of inner sequence standard => standard.StandardID
. If value of both the key field is matched then include that element into result.
The last parameter in Join method is an expression to formulate the result. In the above example, result selector includes StudentName and StandardName property of both the sequence.
StandardID Key of both the sequences (collections) must match otherwise the item will not be included in the result. For example, Ron is not associated with any standard so Ron is not included in the result collection. innerJoinResult in the above example would contain following elements after execution:
John - Standard 1
Moin - Standard 1
Bill - Standard 2
Ram - Standard 2
Join operator in query syntax works slightly different than method syntax. It requires outer sequence, inner sequence, key selector and result selector. ‘on‘ keyword is used for key selector where left side of ‘equals‘ operator is outerKeySelector and right side of ‘equals‘ is innerKeySelector.
from ... in outerSequence
join ... in innerSequence
on outerKey equals innerKey
select ...
The following example of Join operator in query syntax returns a collection of elements from studentList and standardList if their Student.StandardID
and Standard.StandardID
is match.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13, StandardID =1 },
new Student() { StudentID = 2, StudentName = "Moin", Age = 21, StandardID =1 },
new Student() { StudentID = 3, StudentName = "Bill", Age = 18, StandardID =2 },
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20, StandardID =2 },
new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 }
};
IList<Standard> standardList = new List<Standard>() {
new Standard(){ StandardID = 1, StandardName="Standard 1"},
new Standard(){ StandardID = 2, StandardName="Standard 2"},
new Standard(){ StandardID = 3, StandardName="Standard 3"}
};
var innerJoin = from s in studentList // outer sequence
join st in standardList //inner sequence
on s.StandardID equals st.StandardID // key selector
select new { // result selector
StudentName = s.StudentName,
StandardName = st.StandardName
};
John - Standard 1
Moin - Standard 1
Bill - Standard 2
Ram - Standard 2
==
is not valid.from... in outerSequence
join... in innerSequence
on outerKey equals innerKey
select ...
We have seen the Join operator in the previous section. The GroupJoin operator performs the same task as Join operator except that GroupJoin returns a result in group based on specified group key. The GroupJoin operator joins two sequences based on key and groups the result by matching key and then returns the collection of grouped result and key.
GroupJoin requires same parameters as Join. GroupJoin has following two overload methods:
public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector);
public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector, IEqualityComparer<TKey> comparer);
As you can see in the first overload method takes five input parameters (except the first ‘this‘ parameter): 1) outer 2) inner 3) outerKeySelector 4) innerKeySelector 5) resultSelector. Please notice that resultSelector is of Func delegate type that has second input parameter as IEnumerable type for inner sequence.
Now, let‘s understand GroupJoin using following Student and Standard class where Student class includes StandardID that matches with StandardID of Standard class.
public class Student{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int StandardID { get; set; }
}
public class Standard{
public int StandardID { get; set; }
public string StandardName { get; set; }
}
Consider the following GroupJoin query example.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", StandardID =1 },
new Student() { StudentID = 2, StudentName = "Moin", StandardID =1 },
new Student() { StudentID = 3, StudentName = "Bill", StandardID =2 },
new Student() { StudentID = 4, StudentName = "Ram", StandardID =2 },
new Student() { StudentID = 5, StudentName = "Ron" }
};
IList<Standard> standardList = new List<Standard>() {
new Standard(){ StandardID = 1, StandardName="Standard 1"},
new Standard(){ StandardID = 2, StandardName="Standard 2"},
new Standard(){ StandardID = 3, StandardName="Standard 3"}
};
var groupJoin = standardList.GroupJoin(studentList, //inner sequence
std => std.StandardID, //outerKeySelector
s => s.StandardID, //innerKeySelector
(std, studentsGroup) => new // resultSelector
{
Students = studentsGroup,
StandarFulldName = std.StandardName
});
foreach (var item in groupJoin)
{
Console.WriteLine(item.StandarFulldName );
foreach(var stud in item.Students)
Console.WriteLine(stud.StudentName);
}
Standard 1:
John,
Moin,
Standard 2:
Bill,
Ram,
Standard 3:
In the above example of GroupJoin query, standardList is the outer sequence, because the query starts from it. The first parameter in GroupJoin method is to specify the inner sequence, which is studentList in the above example. The second and third parameters of the GroupJoin() method are to specify a field whose value should be matched using lambda expression, in order to include element in the result. The key selector for the outer sequence standard => standard.StandardID
indicates that StandardID field of each elements in standardList should be match with the key of inner sequence studentList student => student.StandardID
. If value of both the key field is matched then include that element into grouped collection studentsGroup where key would be StandardID.
The last parameter in Join method is an expression to formulate the result. In the above example, result selector includes grouped collection studentGroup and StandardName.
The following image illustrate that inner sequence grouped into studentsGroup collection for matching StandardID key and that grouped collection can be used to formulate the result.
Resultset would include an anonymous objects that has the Students and StandardFullName properties. Students property will be a collection of Students whose StandardID matches with Standard.StandardID.
GroupJoin Result in Debug View
You can access the result using a ‘foreach‘ loop. Each element will have the StandardFullName & Students property, where Students will be a collection.
foreach (var item in groupJoinResult)
{
Console.WriteLine(item.StandarFulldName );
foreach(var stud in item.Students)
Console.WriteLine(stud.StudentName);
}
Standard 1:
John,
Moin,
Standard 2:
Bill,
Ram,
Standard 3:
GroupJoin operator in query syntax works slightly different than method syntax. It requires an outer sequence, inner sequence, key selector and result selector. ‘on‘ keyword is used for key selector where the left side of ‘equals‘ operator is the outerKeySelector and the right side of ‘equals‘ is the innerKeySelector. Use the into keyword to create the grouped collection.
from ... in outerSequence
join ... in innerSequence
on outerKey equals innerKey
into groupedCollection
select ...
The following example demonstrates the GroupJoin in query syntax.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13, StandardID =1 },
new Student() { StudentID = 2, StudentName = "Moin", Age = 21, StandardID =1 },
new Student() { StudentID = 3, StudentName = "Bill", Age = 18, StandardID =2 },
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20, StandardID =2 },
new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 }
};
IList<Standard> standardList = new List<Standard>() {
new Standard(){ StandardID = 1, StandardName="Standard 1"},
new Standard(){ StandardID = 2, StandardName="Standard 2"},
new Standard(){ StandardID = 3, StandardName="Standard 3"}
};
var groupJoin = from std in standardList
join s in studentList
on std.StandardID equals s.StandardID
into studentGroup
select new {
Students = studentGroup ,
StandardName = std.StandardName
};
foreach (var item in groupJoin)
{
Console.WriteLine(item.StandarFulldName );
foreach(var stud in item.Students)
Console.WriteLine(stud.StudentName);
}
Standard 1:
John,
Moin,
Standard 2:
Bill,
Ram,
Standard 3:
The grouping operators do the same thing as the GroupBy clause of SQL query. The grouping operators create a group of elements based on the given key. This group is contained in a special type of collection that implements an IGrouping<TKey,TSource> interface where TKey is a key value, on which the group has been formed and TSource is the collection of elements that matches with the grouping key value.
Grouping Operators | Description |
---|---|
GroupBy | The GroupBy operator returns groups of elements based on some key value. Each group is represented by IGrouping<TKey, TElement> object. |
ToLookup | ToLookup is the same as GroupBy; the only difference is the execution of GroupBy is deferred whereas ToLookup execution is immediate. |
The GroupBy operator returns a group of elements from the given collection based on some key value. Each group is represented by IGrouping<TKey, TElement> object. Also, the GroupBy method has eight overload methods, so you can use appropriate extension method based on your requirement in method syntax.
The result of GroupBy operators is a collection of groups. For example, GroupBy returns IEnumerable<IGrouping<TKey,Student>> from the Student collection:
The following example creates a groups of students who have same age. Students of the same age will be in the same collection and each grouped collection will have a key and inner collection, where the key will be the age and the inner collection will include students whose age is matched with a key.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Abram" , Age = 21 }
};
var groupedResult = from s in studentList
group s by s.Age;
//iterate each group
foreach (var ageGroup in groupedResult)
{
Console.WriteLine("Age Group: {0}", ageGroup .Key); //Each group has a key
foreach(Student s in ageGroup) // Each group has inner collection
Console.WriteLine("Student Name: {0}", s.StudentName);
}
AgeGroup: 18
StudentName: John
StudentName: Bill
AgeGroup: 21
StudentName: Steve
StudentName: Abram
AgeGroup: 20
StudentName: Ram
As you can see in the above example, you can iterate the group using a ‘foreach‘ loop, where each group contains a key and inner collection. The following figure shows the result in debug view.
Grouped collection with key and inner collection
The GroupBy() extension method works the same way in the method syntax. Specify the lambda expression for key selector field name in GroupBy extension method.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Abram" , Age = 21 }
};
var groupedResult = studentList.GroupBy(s => s.Age);
foreach (var ageGroup in groupedResult)
{
Console.WriteLine("Age Group: {0}", ageGroup.Key); //Each group has a key
foreach(Student s in ageGroup) //Each group has a inner collection
Console.WriteLine("Student Name: {0}", s.StudentName);
}
AgeGroup: 18
StudentName: John
StudentName: Bill
AgeGroup: 21
StudentName: Steve
StudentName: Abram
AgeGroup: 20
StudentName: Ram
ToLookup is the same as GroupBy; the only difference is GroupBy execution is deferred, whereas ToLookup execution is immediate. Also, ToLookup is only applicable in Method syntax. ToLookup is not supported in the query syntax.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Abram" , Age = 21 }
};
var lookupResult = studentList.ToLookup(s => s.age);
foreach (var group in lookupResult)
{
Console.WriteLine("Age Group: {0}", group.Key); //Each group has a key
foreach(Student s in group) //Each group has a inner collection
Console.WriteLine("Student Name: {0}", s.StudentName);
}
The quantifier operators evaluate elements of the sequence on some condition and return a boolean value to indicate that some or all elements satisfy the condition.
Operator | Description |
---|---|
All | Checks if all the elements in a sequence satisfies the specified condition |
Any | Checks if any of the elements in a sequence satisfies the specified condition |
Contain | Checks if the sequence contains a specific element |
The All operator evalutes each elements in the given collection on a specified condition and returns True if all the elements satisfy a condition.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
// checks whether all the students are teenagers
bool areAllStudentsTeenAger = studentList.All(s => s.Age > 12 && s.Age < 20);
Console.WriteLine(areAllStudentsTeenAger);
Dim areAllStudentsTeenAger = studentList.All(Function(s) s.Age > 12 And s.Age < 20)
Any checks whether any element satisfy given condition or not? In the following example, Any operation is used to check whether any student is teen ager or not.
bool isAnyStudentTeenAger = studentList.Any(s => s.age > 12 && s.age < 20);
Dim isAnyStudentTeenAger = studentList.Any(Function(s) s.Age > 12 And s.Age < 20)
The aggregation operators perform mathematical operations like Average, Aggregate, Count, Max, Min and Sum, on the numeric property of the elements in the collection.
Method | Description |
---|---|
Aggregate | Performs a custom aggregation operation on the values in the collection. |
Average | calculates the average of the numeric items in the collection. |
Count | Counts the elements in a collection. |
LongCount | Counts the elements in a collection. |
Max | Finds the largest value in the collection. |
Min | Finds the smallest value in the collection. |
Sum | Calculates sum of the values in the collection. |
The Aggregate method performs an accumulate operation. Aggregate extension method has the following overload methods:
public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func); public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func); public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector);
The following example demonstrates Aggregate method that returns comma seperated elements of the string list.
IList<String> strList = new List<String>() { "One", "Two", "Three", "Four", "Five"};
var commaSeperatedString = strList.Aggregate((s1, s2) => s1 + ", " + s2);
Console.WriteLine(commaSeperatedString);
In the above example, Aggregate extension method returns comma separated strings from strList collection. The following image illustrates the whole aggregate operation performed in the above example.
Aggregate extension methodAs per the above figure, first item of strList "One" will be pass as s1 and rest of the items will be passed as s2. The lambda expression (s1, s2) => s1 + ", " + s2
will be treated like s1 = s1 + ", " + s1
where s1 will be accumulated for each item in the collection. Thus, Aggregate method will return comma separated string.
Dim strList As IList(Of String) = New List(Of String) From {
"One",
"Two",
"Three",
"Four",
"Five"
}
Dim commaSeparatedString = strList.Aggregate(Function(s1, s2) s1 + ", " + s2)
The second overload method of Aggregate requires first parameter for seed value to accumulate. Second parameter is Func type delegate:TAccumulate Aggregate<TSource, TAccumulate>(TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);
.
The following example uses string as a seed value in the Aggregate extension method.
// Student collection
IList<Student> studentList = new List<Student>>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 }
};
string commaSeparatedStudentNames = studentList.Aggregate<Student, string>(
"Student Names: ", // seed value
(str, s) => str += s.StudentName + "," );
Console.WriteLine(commaSeparatedStudentNames);
// Student collection
Dim studentList = New List(Of Student) From {
New Student() With {.StudentID = 1, .StudentName = "John", .Age = 13},
New Student() With {.StudentID = 2, .StudentName = "Moin", .Age = 21},
New Student() With {.StudentID = 3, .StudentName = "Bill", .Age = 18},
New Student() With {.StudentID = 4, .StudentName = "Ram", .Age = 20},
New Student() With {.StudentID = 5, .StudentName = "Ron", .Age = 15}
}
Dim commaSeparatedStudentNames = studentList.Aggregate(Of String)(
"Student Names: ",
Function(str, s) str + s.StudentName + ",")
Console.WriteLine(commaSeparatedStudentNames);
In the above example, the first parameter of the Aggregate method is the "Student Names: " string that will be accumulated with all student names. The comma in the lambda expression will be passed as a second parameter.
The following example use Aggregate operator to add the age of all the students.
// Student collection
IList<Student> studentList = new List<Student>>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 }
};
int SumOfStudentsAge = studentList.Aggregate<Student, int>(0,
(totalAge, s) => totalAge += s.Age );
Now, let‘s see third overload method that required the third parameter of the Func delegate expression for result selector, so that you can formulate the result.
Consider the following example.
IList<Student> studentList = new List<Student>>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 }
};
string commaSeparatedStudentNames = studentList.Aggregate<Student, string,string>(
String.Empty, // seed value
(str, s) => str += s.StudentName + ",", // returns result using seed value, String.Empty goes to lambda expression as str
str => str.Substring(0,str.Length - 1 )); // result selector that removes last comma
Console.WriteLine(commaSeparatedStudentNames);
In the above example, we have specified a lambda expression str => str.Substring(0,str.Length - 1 )
which will remove the last comma in the string result. Below is the same example in VB.Net.
// Student collection
Dim studentList = New List(Of Student) From {
New Student() With {.StudentID = 1, .StudentName = "John", .Age = 13},
New Student() With {.StudentID = 2, .StudentName = "Moin", .Age = 21},
New Student() With {.StudentID = 3, .StudentName = "Bill", .Age = 18},
New Student() With {.StudentID = 4, .StudentName = "Ram", .Age = 20},
New Student() With {.StudentID = 5, .StudentName = "Ron", .Age = 15}
}
Dim commaSeparatedStudentNames = studentList.Aggregate(Of String, String)(
String.Empty,
Function(str, s) str + s.StudentName + ",",
Function(str) str.Substring(0, str.Length - 1))
Console.WriteLine(commaSeparatedStudentNames);
The Max operator returns the largest numeric element from a collection.
The following example demonstrates Max() on primitive collection.
IList<int> intList = new List<int>() { 10, 21, 30, 45, 50, 87 };
var largest = intList.Max();
Console.WriteLine("Largest Element: {0}", largest);
var largestEvenElements = intList.Max(i => {
if(i%2 == 0)
return i;
return 0;
});
Console.WriteLine("Largest Even Element: {0}", largestEvenElements );
The following example demonstrates Max() method on the complex type collection.
IList<Student> studentList = new List<Student>>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 }
};
var oldest = studentList.Max(s => s.Age);
Console.WriteLine("Oldest Student Age: {0}", oldest);
Dim studentList = New List(Of Student) From {
New Student() With {.StudentID = 1, .StudentName = "John", .Age = 13},
New Student() With {.StudentID = 2, .StudentName = "Moin", .Age = 21},
New Student() With {.StudentID = 3, .StudentName = "Bill", .Age = 18},
New Student() With {.StudentID = 4, .StudentName = "Ram", .Age = 20},
New Student() With {.StudentID = 5, .StudentName = "Ron", .Age = 15}
}
Dim oldest = studentList.Max(Function(s) s.Age)
Console.WriteLine("Oldest Student Age: {0}", oldest)
Max returns a result of any data type. The following example shows how you can find a student with the longest name in the collection:
public class Student : IComparable<Student>
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
public int StandardID { get; set; }
public int CompareTo(Student other)
{
if (this.StudentName.Length >= other.StudentName.Length)
return 1;
return 0;
}
}
class Program
{
static void Main(string[] args)
{
// Student collection
IList<Student> studentList = new List<Student>>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
new Student() { StudentID = 5, StudentName = "Steve" , Age = 15 }
};
var studentWithLongName = studentList.Max();
Console.WriteLine("Student ID: {0}, Student Name: {1}",
.StudentID, studentWithLongName.StudentName)
}
}
As per the above example, to find the student with the longest name, you need to implement IComparable<T> interface and compare student names‘ length in CompareTo method. So now, you can use Max() method which will use CompareTo method in order to return appropriate result.
There is only one equality operator: SequenceEqual. The SequenceEqual method checks whether the number of elements, value of each element and order of elements in two collections are equal or not.
If the collection contains elements of primitive data types then it compares the values and number of elements, whereas collection with complex type elements, checks the references of the objects. So, if the objects have the same reference then they considered as equal otherwise they are considered not equal.
The following example demonstrates the SequenceEqual method with the collection of primitive data types.
IList<string> strList1 = new List<string>(){"One", "Two", "Three", "Four", "Three"};
IList<string> strList2 = new List<string>(){"One", "Two", "Three", "Four", "Three"};
bool isEqual = strList1.SequenceEqual(strList2); // returns true
Console.WriteLine(isEqual);
true
If the order of elements are not the same then SequenceEqual() method returns false.
IList<string> strList1 = new List<string>(){"One", "Two", "Three", "Four", "Three"};
IList<string> strList2 = new List<string>(){ "Two", "One", "Three", "Four", "Three"};
bool isEqual = strList1.SequenceEqual(strList2); // returns false
Console.WriteLine(isEqual);
false
The SequenceEqual extension method checks the references of two objects to determine whether two sequences are equal or not. This may give wrong result. Consider following example:
Student std = new Student() { StudentID = 1, StudentName = "Bill" };
IList<Student> studentList1 = new List<Student>(){ std };
IList<Student> studentList2 = new List<Student>(){ std };
bool isEqual = studentList1.SequenceEqual(studentList2); // returns true
Student std1 = new Student() { StudentID = 1, StudentName = "Bill" };
Student std2 = new Student() { StudentID = 1, StudentName = "Bill" };
IList<Student> studentList3 = new List<Student>(){ std1};
IList<Student> studentList4 = new List<Student>(){ std2 };
isEqual = studentList3.SequenceEqual(studentList4);// returns false
In the above example, studentList1 and studentList2 contains the same student object, std. So studentList1.SequenceEqual(studentList2)
returns true. But, stdList1 and stdList2 contains two seperate student object, std1 and std2. So now, stdList1.SequenceEqual(stdList2)
will return false even if std1 and std2 contain the same value.
To compare the values of two collection of complex type (reference type or object), you need to implement IEqualityComperar<T> interface as shown below.
class StudentComparer : IEqualityComparer<Student>
{
public bool Equals(Student x, Student y)
{
if (x.StudentID == y.StudentID && x.StudentName.ToLower() == y.StudentName.ToLower())
return true;
return false;
}
public int GetHashCode(Student obj)
{
return obj.GetHashCode();
}
}
Now, you can use above StudentComparer class in SequenceEqual extension method as a second parameter to compare the values:
IList<Student> studentList1 = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
IList<Student> studentList2 = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
// following returns true
bool isEqual = studentList1.SequenceEqual(studentList2, new StudentComparer());
The Concat() method appends two sequences of the same type and returns a new sequence (collection).
IList<string> collection1 = new List<string>() { "One", "Two", "Three" };
IList<string> collection2 = new List<string>() { "Five", "Six"};
var collection3 = collection1.Concat(collection2);
foreach (string str in collection3)
Console.WriteLine(str);
One
Two
Three
Five
Six
IList<int> collection1 = new List<int>() { 1, 2, 3 };
IList<int> collection2 = new List<int>() { 4, 5, 6 };
var collection3 = collection1.Concat(collection2);
foreach (int i in collection3)
Console.WriteLine(i);
1
2
3
4
5
6
The DefaultIfEmpty() method returns a new collection with the default value if the given collection on which DefaultIfEmpty() is invoked is empty.
Another overload method of DefaultIfEmpty() takes a value parameter that should be replaced with default value.
Consider the following example.
IList<string> emptyList = new List<string>();
var newList1 = emptyList.DefaultIfEmpty();
var newList2 = emptyList.DefaultIfEmpty("None");
Console.WriteLine("Count: {0}" , newList1.Count());
Console.WriteLine("Value: {0}" , newList1.ElementAt(0));
Console.WriteLine("Count: {0}" , newList2.Count());
Console.WriteLine("Value: {0}" , newList2.ElementAt(0));
Count: 1
Value:
Count: 1
Value: None
In the above example, emptyList.DefaultIfEmpty()
returns a new string collection with one element whose value is null because null is a default value of string. Another method emptyList.DefaultIfEmpty("None")
returns a string collection with one element whose value is "None" instead of null.
The following example demonstrates calling DefaultIfEmpty on int collection.
IList<int> emptyList = new List<int>();
var newList1 = emptyList.DefaultIfEmpty();
var newList2 = emptyList.DefaultIfEmpty(100);
Console.WriteLine("Count: {0}" , newList1.Count());
Console.WriteLine("Value: {0}" , newList1.ElementAt(0));
Console.WriteLine("Count: {0}" , newList2.Count());
Console.WriteLine("Value: {0}" , newList2.ElementAt(0));
Count: 1
Value: 0
Count: 1
Value: 100
The following example demonstrates DefaultIfEmpty() method on complex type collection.
IList<Student> emptyStudentList = new List<Student>();
var newStudentList1 = studentList.DefaultIfEmpty(new Student());
var newStudentList2 = studentList.DefaultIfEmpty(new Student(){
StudentID = 0,
StudentName = "" });
Console.WriteLine("Count: {0} ", newStudentList1.Count());
Console.WriteLine("Student ID: {0} ", newStudentList1.ElementAt(0));
Console.WriteLine("Count: {0} ", newStudentList2.Count());
Console.WriteLine("Student ID: {0} ", newStudentList2.ElementAt(0).StudentID);
Count: 1
Student ID:
Count: 1
Student ID: 0
LINQ includes generation operators DefaultIfEmpty, Empty, Range & Repeat. The Empty, Range & Repeat methods are not extension methods for IEnumerable or IQueryable but they are simply static methods defined in a static class Enumerable.
Method | Description |
---|---|
Empty | Returns an empty collection |
Range | Generates collection of IEnumerable<T> type with specified number of elements with sequential values, starting from first element. |
Repeat | Generates a collection of IEnumerable<T> type with specified number of elements and each element contains same specified value. |
The Empty() method is not an extension method of IEnumerable or IQueryable like other LINQ methods. It is a static method included in Enumerable static class. So, you can call it the same way as other static methods like Enumerable.Empty<TResult>(). The Empty() method returns an empty collection of a specified type as shown below.
var emptyCollection1 = Enumerable.Empty<string>();
var emptyCollection2 = Enumerable.Empty<Student>();
Console.WriteLine("Count: {0} ", emptyCollection1.Count());
Console.WriteLine("Type: {0} ", emptyCollection1.GetType().Name );
Console.WriteLine("Count: {0} ",emptyCollection2.Count());
Console.WriteLine("Type: {0} ", emptyCollection2.GetType().Name );
Type: String[]
Count: 0
Type: Student[]
Count: 0
The Range() method returns a collection of IEnumerable<T> type with specified number of elements and sequential values starting from the first element.
var intCollection = Enumerable.Range(10, 10);
Console.WriteLine("Total Count: {0} ", intCollection.Count());
for(int i = 0; i < intCollection.Count(); i++)
Console.WriteLine("Value at index {0} : {1}", i, intCollection.ElementAt(i));
Total Count: 10
Value at index 0 : 10
Value at index 1 : 11
Value at index 2 : 12
Value at index 3 : 13
Value at index 4 : 14
Value at index 5 : 15
Value at index 6 : 16
Value at index 7 : 17
Value at index 8 : 18
Value at index 9 : 19
In the above example, Enumerable.Range(10, 10)
creates collection with 10 integer elements with the sequential values starting from 10. First parameter specifies the starting value of elements and second parameter specifies the number of elements to create.
The Repeat() method generates a collection of IEnumerable<T> type with specified number of elements and each element contains same specified value.
var intCollection = Enumerable.Repeat<int>(10, 10);
Console.WriteLine("Total Count: {0} ", intCollection.Count());
for(int i = 0; i < intCollection.Count(); i++)
Console.WriteLine("Value at index {0} : {1}", i, intCollection.ElementAt(i));
Total Count: 10
Value at index 0: 10
Value at index 1: 10
Value at index 2: 10
Value at index 3: 10
Value at index 4: 10
Value at index 5: 10
Value at index 6: 10
Value at index 7: 10
Value at index 8: 10
Value at index 9: 10
In the above example, Enumerable.Repeat<int>(10, 10)
creates collection with 100 integer type elements with the repeated value of 10. First parameter specifies the values of all the elements and second parameter specifies the number of elements to create.
The following table lists all Set operators available in LINQ.
Set Operators | Usage |
---|---|
Distinct | Returns distinct values from a collection. |
Except | Returns the difference between two sequences, which means the elements of one collection that do not appear in the second collection. |
Intersect | Returns the intersection of two sequences, which means elements that appear in both the collections. |
Union | Returns unique elements from two sequences, which means unique elements that appear in either of the two sequences. |
The following figure shows how each set operators works on the collections:
The Distinct extension method returns a new collection of unique elements from the given collection.
IList<string> strList = new List<string>(){ "One", "Two", "Three", "Two", "Three" };
IList<int> intList = new List<int>(){ 1, 2, 3, 2, 4, 4, 3, 5 };
var distinctList1 = strList.Distinct();
foreach(var str in distinctList1)
Console.WriteLine(str);
var distinctList2 = intList.Distinct();
foreach(var i in distinctList2)
Console.WriteLine(i);
One
Two
Three
1
2
3
4
5
The Distinct extension method doesn‘t compare values of complex type objects. You need to implement IEqualityComparer<T>
interface in order to compare the values of complex types. In the following example, StudentComparer
class implements IEqualityComparer<Student>
to compare Student<
objects.
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
}
class StudentComparer : IEqualityComparer<Student>
{
public bool Equals(Student x, Student y)
{
if (x.StudentID == y.StudentID
&& x.StudentName.ToLower() == y.StudentName.ToLower())
return true;
return false;
}
public int GetHashCode(Student obj)
{
return obj.StudentID.GetHashCode();
}
}
Now, you can pass an object of the above StudentComparer
class in the Distinct() method as a parameter to compare the Student
objects as shown below.
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
var distinctStudents = studentList.Distinct(new StudentComparer());
foreach(Student std in distinctStudents)
Console.WriteLine(std.StudentName);
John
Steve
Bill
Ron
The Distinct operator is Not Supported in C# Query syntax. However, you can use Distinct method of query variable or wrap whole query into brackets and then call Distinct().
Use the Distinct keyword in VB.Net query syntax:
Dim strList = New List(Of string) From {"One", "Three", "Two", "Two", "One" }
Dim distinctStr = From s In strList _
Select s Distinct
The Except() method requires two collections. It returns a new collection with elements from the first collection which do not exist in the second collection (parameter collection).
IList<string> strList1 = new List<string>(){"One", "Two", "Three", "Four", "Five" };
IList<string> strList2 = new List<string>(){"Four", "Five", "Six", "Seven", "Eight"};
var result = strList1.Except(strList2);
foreach(string str in result)
Console.WriteLine(str);
One
Two
Three
Except extension method doesn‘t return the correct result for the collection of complex types. You need to implement IEqualityComparer interface in order to get the correct result from Except method.
Implement IEqualityComparer interface for Student class as shown below:
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
}
class StudentComparer : IEqualityComparer<Student>
{
public bool Equals(Student x, Student y)
{
if (x.StudentID == y.StudentID && x.StudentName.ToLower() == y.StudentName.ToLower())
return true;
return false;
}
public int GetHashCode(Student obj)
{
return obj.StudentID.GetHashCode();
}
}
Now, you can pass above StudentComparer class in Except extension method in order to get the correct result:
IList<Student> studentList1 = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
IList<Student> studentList2 = new List<Student>() {
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
var resultedCol = studentList1.Except(studentList2,new StudentComparer());
foreach(Student std in resultedCol)
Console.WriteLine(std.StudentName);
John
Steve
The Except operator is Not Supported in C# & VB.Net Query syntax. However, you can use Distinct method on query variable or wrap whole query into brackets and then call Except().
The following figure shows how each set operators works on the collections:
The Intersect extension method requires two collections. It returns a new collection that includes common elements that exists in both the collection. Consider the following example.
IList<string> strList1 = new List<string>() { "One", "Two", "Three", "Four", "Five" };
IList<string> strList2 = new List<string>() { "Four", "Five", "Six", "Seven", "Eight"};
var result = strList1.Intersect(strList2);
foreach(string str in result)
Console.WriteLine(str);
The Intersect extension method doesn‘t return the correct result for the collection of complex types. You need to implement IEqualityComparer interface in order to get the correct result from Intersect method.
Implement IEqualityComparer interface for Student class as shown below:
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
}
class StudentComparer : IEqualityComparer<Student>
{
public bool Equals(Student x, Student y)
{
if (x.StudentID == y.StudentID &&
x.StudentName.ToLower() == y.StudentName.ToLower())
return true;
return false;
}
public int GetHashCode(Student obj)
{
return obj.StudentID.GetHashCode();
}
}
Now, you can pass above StudentComparer class in the Intersect extension method in order to get the correct result:
IList<Student> studentList1 = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
IList<Student> studentList2 = new List<Student>() {
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
var resultedCol = studentList1.Intersect(studentList2, new StudentComparer());
foreach(Student std in resultedCol)
Console.WriteLine(std.StudentName);
The Intersect operator is Not Supported in C# & VB.Net Query syntax. However, you can use the Intersect method on a query variable or wrap whole query into brackets and then call Intersect().
The following figure shows how each set operators works on the collections:
The Union extension method requires two collections and returns a new collection that includes distinct elements from both the collections. Consider the following example.
IList<string> strList1 = new List<string>() { "One", "Two", "three", "Four" };
IList<string> strList2 = new List<string>() { "Two", "THREE", "Four", "Five" };
var result = strList1.Union(strList2);
foreach(string str in result)
Console.WriteLine(str);
One
Two
three
THREE
Four
Five
The Union extension method doesn‘t return the correct result for the collection of complex types. You need to implement IEqualityComparer interface in order to get the correct result from Union method.
Implement IEqualityComparer interface for Student class as below:
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
}
class StudentComparer : IEqualityComparer<Student>
{
public bool Equals(Student x, Student y)
{
if (x.StudentID == y.StudentID && x.StudentName.ToLower() == y.StudentName.ToLower())
return true;
return false;
}
public int GetHashCode(Student obj)
{
return obj.StudentID.GetHashCode();
}
}
Now, you can pass above StudentComparer class in the Union extension method to get the correct result:
IList<Student> studentList1 = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
IList<Student> studentList2 = new List<Student>() {
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }
};
var resultedCol = studentList1.Union(studentList2, new StudentComparer());
foreach(Student std in resultedCol)
Console.WriteLine(std.StudentName);
John
Steve
Bill
Ron
The Union operator is Not Supported in C# & VB.Net Query syntax. However, you can use Union method on query variable or wrap whole query into brackets and then call Union().
The following figure shows how each set operators works on the collections:
Partitioning operators split the sequence (collection) into two parts and return one of the parts.
Method | Description |
---|---|
Skip | Skips elements up to a specified position starting from the first element in a sequence. |
SkipWhile | Skips elements based on a condition until an element does not satisfy the condition. If the first element itself doesn‘t satisfy the condition, it then skips 0 elements and returns all the elements in the sequence. |
Take | Takes elements up to a specified position starting from the first element in a sequence. |
TakeWhile | Returns elements from the first element until an element does not satisfy the condition. If the first element itself doesn‘t satisfy the condition then returns an empty collection. |
The Skip() method skips the specified number of element starting from first element and returns rest of the elements.
IList<string> strList = new List<string>(){ "One", "Two", "Three", "Four", "Five" };
var newList = strList.Skip(2);
foreach(var str in newList)
Console.WriteLine(str);
Three
Four
Five
The Skip & SkipWhile operator is Not Supported in C# query syntax. However, you can use Skip/SkipWhile method on a query variable or wrap whole query into brackets and then call Skip/SkipWhile.
The following example demonstrates skip operator in query syntax - VB.NET
Dim skipResult = From s In studentList
Skip 3
Select s
As the name suggests, the SkipWhile() extension method in LINQ skip elements in the collection till the specified condition is true. It returns a new collection that includes all the remaining elements once the specified condition becomes false for any element.
The SkipWhile() method has two overload methods. One method accepts the predicate of Func<TSource, bool>
type and other overload method accepts the predicate Func<TSource, int, bool>
type that pass the index of an element.
In the following example, SkipWhile() method skips all elements till it finds a string whose length is equal or more than 4 characters.
IList<string> strList = new List<string>() {
"One",
"Two",
"Three",
"Four",
"Five",
"Six" };
var resultList = strList.SkipWhile(s => s.Length < 4);
foreach(string str in resultList)
Console.WriteLine(str);
Three
Four
Five
Six
In the above example, SkipWhile() skips first two elements because their length is less than 3 and finds third element whose length is equal or more than 4. Once it finds any element whose length is equal or more than 4 characters then it will not skip any other elements even if they are less than 4 characters.
Now, consider the following example where SkipWhile() does not skip any elements because the specified condition is false for the first element.
IList<string> strList = new List<string>() {
"Three",
"One",
"Two",
"Four",
"Five",
"Six" };
var resultList = strList.SkipWhile(s => s.Length < 4);
foreach(string str in resultList)
Console.WriteLine(str);
Three
One
Two
Four
Five
Six
The second overload of SkipWhile passes an index of each elements. Consider the following example.
IList<string> strList = new List<string>() {
"One",
"Two",
"Three",
"Four",
"Five",
"Six" };
var result = strList.SkipWhile((s, i) => s.Length > i);
foreach(string str in result)
Console.WriteLine(str);
Five
Six
In the above example, the lambda expression includes element and index of an elements as a parameter. It skips all the elements till the length of a string element is greater than it‘s index.
Skip & SkipWhile operator is NOT Supported in C# query syntax. However, you can use Skip/SkipWhile method on a query variable or wrap whole query into brackets and then call Skip/SkipWhile().
Dim strList = New List(Of string) From {
"One",
"Two",
"Three",
"Four",
"Five",
"Six" }
Dim skipWhileResult = From s In studentList
Skip While s.Length < 4
Select s
Three
Four
Five
Six
Partitioning operators split the sequence (collection) into two parts and returns one of the parts.
Method | Description |
---|---|
Skip | Skips elements up to a specified position starting from the first element in a sequence. |
SkipWhile | Skips elements based on a condition until an element does not satisfy the condition. If the first element itself doesn‘t satisfy the condition, it then skips 0 elements and returns all the elements in the sequence. |
Take | Takes elements up to a specified position starting from the first element in a sequence. |
TakeWhile | Returns elements from the given collection until the specified condition is true. If the first element itself doesn‘t satisfy the condition then returns an empty collection. |
The Take() extension method returns the specified number of elements starting from the first element.
IList<string> strList = new List<string>(){ "One", "Two", "Three", "Four", "Five" };
var newList = strList.Take(2);
foreach(var str in newList)
Console.WriteLine(str);
Take & TakeWhile operator is Not Supported in C# query syntax. However, you can use Take/TakeWhile method on query variable or wrap whole query into brackets and then call Take/TakeWhile().
Dim takeResult = From s In studentList
Take 3
Select s
The TakeShile() extension method returns elements from the given collection until the specified condition is true. If the first element itself doesn‘t satisfy the condition then returns an empty collection.
The TakeWhile method has two overload methods. One method accepts the predicate of Func<TSource, bool>
type and the other overload method accepts the predicate Func<TSource, int, bool>
type that passes the index of element.
In the following example, TakeWhile() method returns a new collection that includes all the elements till it finds a string whose length less than 4 characters.
IList<string> strList = new List<string>() {
"Three",
"Four",
"Five",
"Hundred" };
var result = strList.TakeWhile(s => s.Length > 4);
foreach(string str in result)
Console.WriteLine(str);
In the above example, TakeWhile() includes only first element because second string element does not satisfied the condition.
TakeWhile also passes an index of current element in predicate function. Following example of TakeWhile method takes elements till length of string element is greater than it‘s index
IList<string> strList = new List<string>() {
"One",
"Two",
"Three",
"Four",
"Five",
"Six" };
var resultList = strList.TakeWhile((s, i) => s.Length > i);
foreach(string str in resultList)
Console.WriteLine(str);
The Conversion operators in LINQ are useful in converting the type of the elements in a sequence (collection). There are three types of conversion operators: As operators (AsEnumerable and AsQueryable), To operators (ToArray, ToDictionary, ToList and ToLookup), and Casting operators (Cast and OfType).
The following table lists all the conversion operators.
Method | Description |
---|---|
AsEnumerable | Returns the input sequence as IEnumerable<t> |
AsQueryable | Converts IEnumerable to IQueryable, to simulate a remote query provider |
Cast | Coverts a non-generic collection to a generic collection (IEnumerable to IEnumerable<T>) |
OfType | Filters a collection based on a specified type |
ToArray | Converts a collection to an array |
ToDictionary | Puts elements into a Dictionary based on key selector function |
ToList | Converts collection to List |
ToLookup | Groups elements into an Lookup<TKey,TElement> |
The AsEnumerable and AsQueryable methods cast or convert a source object to IEnumerable<T> or IQueryable<T> respectively.
Consider the following example: (courtesy: Jon Skeet)
class Program
{
static void ReportTypeProperties<T>(T obj)
{
Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
Console.WriteLine("Actual type: {0}", obj.GetType().Name);
}
static void Main(string[] args)
{
Student[] studentArray = {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 31 } ,
};
ReportTypeProperties( studentArray);
ReportTypeProperties(studentArray.AsEnumerable());
ReportTypeProperties(studentArray.AsQueryable());
}
}
As you can see in the above example AsEnumerable and AsQueryable methods convert compile time type to IEnumerable and IQueryable respectively
Visit stackoverflow for detail information on AsEnumerable and AsQueryable method.
Cast does the same thing as AsEnumerable<T>. It cast the source object into IEnumerable<T>.
class Program
{
static void ReportTypeProperties<T>(T obj)
{
Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
Console.WriteLine("Actual type: {0}", obj.GetType().Name);
}
static void Main(string[] args)
{
Student[] studentArray = {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 25 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 31 } ,
};
ReportTypeProperties( studentArray);
ReportTypeProperties(studentArray.Cast<Student>());
}
}
studentArray.Cast<Student>()
is the same as (IEnumerable<Student>)studentArray
but Cast<Student>() is more readable.
As the name suggests, ToArray(), ToList(), ToDictionary() method converts a source object into an array, List or Dictionary respectively.
To operators force the execution of the query. It forces the remote query provider to execute a query and get the result from the underlying data source e.g. SQL Server database.
IList<string> strList = new List<string>() {
"One",
"Two",
"Three",
"Four",
"Three"
};
string[] strArray = strList.ToArray<string>();// converts List to Array
IList<string> list = strArray.ToList<string>(); // converts array into list
ToDictionary - Converts a Generic list to a generic dictionary:
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , age = 20 } ,
new Student() { StudentID = 5, StudentName = "Ron" , age = 21 }
};
//following converts list into dictionary where StudentId is a key
IDictionary<int, Student> studentDict =
studentList.ToDictionary<Student, int>(s => s.StudentID);
foreach(var key in studentDict.Keys)
Console.WriteLine("Key: {0}, Value: {1}",
key, (studentDict[key] as Student).StudentName);
The following figure shows how studentDict in the above example contains a key-value pair, where key is a StudentID and the value is Student object.
LINQ-ToDictionary OperatorThe ‘let‘ keyword is useful in query syntax. It projects a new range variable, allows re-use of the expression and makes the query more readable.
For example, you can compare string values and select the lowercase string value as shown below:
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 21 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 21 }
};
var lowercaseStudentNames = from s in studentList
where s.StudentName.ToLower().StartsWith("r")
select s.StudentName.ToLower();
As you can see, the ToLower() method is used multiple times in the above query. The following example use ‘let‘ to introduce new variable ‘lowercaseStudentName‘ that will be then used in every where. Thus, let keyword to make the query more readable.
var lowercaseStudentNames = from s in studentList
let lowercaseStudentName = s.StudentName.ToLower()
where lowercaseStudentName.StartsWith("r")
select lowercaseStudentName;
foreach (var name in lowercaseStudentNames)
Console.WriteLine(name);
Dim lowercaseStudentNames = From s In studentList
Let lowercaseStudentName = s.StudentName.ToLower()
Where lowercaseStudentName.StartsWith("r")
Select lowercaseStudentName;
We have already used the ‘into‘ keyword in grouping. You can also use the ‘into‘ keyword to continue a query after a selectclause.
var teenAgerStudents = from s in studentList
where s.age > 12 && s.age < 20
select s
into teenStudents
where teenStudents.StudentName.StartsWith("B")
select teenStudents;
In the above query, the ‘into‘ keyword introduced a new range variable teenStudents, so the first range variable s goes out of scope. You can write a further query after the into keyword using a new range variable.
Select() 和 SelectMany() 的工作都是依据源值生成一个或多个结果值。
Select() 为每个源值生成一个结果值。因此,总体结果是一个与源集合具有相同元素数目的集合。与之相反,SelectMany() 将生成单一总体结果,其中包含来自每个源值的串联子集合。作为参数传递到 SelectMany() 的转换函数必须为每个源值返回一个可枚举值序列。然后,SelectMany() 将串联这些可枚举序列以创建一个大的序列。
string[] text ={ "Albert was here", "Burke slept late", "Connor is happy" }; var tokens = text.Select(s => s.Split(‘‘)); foreach (string[] line in tokens) foreach (string token in line) Console.Write("{0}.", token); string[] text ={ "Albert was here", "Burke slept late", "Connor is happy" }; var tokens = text.SelectMany(s => s.Split(‘‘)); foreach (string token in tokens) Console.Write("{0}.", token);
下面两个插图演示了这两个方法的操作之间的概念性区别。在每种情况下,假定选择器(转换)函数从每个源值中选择一个由花卉数据组成的数组。
下图描述 Select() 如何返回一个与源集合具有相同元素数目的集合。
下图描述 SelectMany() 如何将中间数组序列串联为一个最终结果值,其中包含每个中间数组中的每个值。
CSDN的例子:
class Bouquet { public List<string> Flowers { get; set; } } static void SelectVsSelectMany() { List<Bouquet> bouquets = new List<Bouquet>() { new Bouquet { Flowers = new List<string> { "sunflower", "daisy", "daffodil", "larkspur" }}, new Bouquet{ Flowers = new List<string> { "tulip", "rose", "orchid" }}, new Bouquet{ Flowers = new List<string> { "gladiolis", "lily", "snapdragon", "aster", "protea" }}, new Bouquet{ Flowers = new List<string> { "larkspur", "lilac", "iris", "dahlia" }} }; // *********** Select *********** IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers); // ********* SelectMany ********* IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers); Console.WriteLine("Results by using Select():"); // Note the extra foreach loop here. foreach (IEnumerable<String> collection in query1) foreach (string item in collection) Console.WriteLine(item); Console.WriteLine("\nResults by using SelectMany():"); foreach (string item in query2) Console.WriteLine(item); /* This code produces the following output: Results by using Select(): sunflower daisy daffodil larkspur tulip rose orchid gladiolis lily snapdragon aster protea larkspur lilac iris dahlia Results by using SelectMany(): sunflower daisy daffodil larkspur tulip rose orchid gladiolis lily snapdragon aster protea larkspur lilac iris dahlia */ }
Select() 和 SelectMany() 的工作都是依据源值生成一个或多个结果值。 Select() 为每个源值生成一个结果值。 因此,总体结果是一个与源集合具有相同元素数目的集合。 与之相反,SelectMany() 将生成单一总体结果,其中包含来自每个源值的串联子集合。
SelectMany()生成的是单一的总体结果。请看SelectMany()函数的实现形式:
public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector) { if (source == null) throw Error.ArgumentNull("source"); if (selector == null) throw Error.ArgumentNull("selector"); return SelectManyIterator<TSource, TResult>(source, selector); } static IEnumerable<TResult> SelectManyIterator<TSource, TResult>(IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector) { foreach (TSource element in source) { foreach (TResult subElement in selector(element)) { yield return subElement; } } }
在代码中的红色部分,可以看出SelectMany()的实现过程。在第一个例子中的SelectMany()。
string[] text ={ "Albert was here", "Burke slept late", "Connor is happy" };
var tokens = text.SelectMany(s => s.Split(‘‘));
text.SelectMany(s => s.Split(‘‘));
可以用SelectManyIterator<TSource, TResult>(IEnumerable<TSource> source,Func<TSource, IEnumerable<TResult>> selector)替换,变成 SelectManyIterator(text,s=>s.Splt(‘ ‘));而在SelectManyIterator内部的代码相应的变为:
foreach (string element in text) { text是string[]数组
foreach (string subElement in s=>s.Split(‘ ‘)) { s.Split(‘ ‘)则编程text元素生成的可枚举数组序列,IEnumerable<string>
yield return subElement;}}
以上转换可以看下图:
上图中selector是Func<TSource,IEnumerable<TResult>>谓语表达式,相当于委托。示例中委托的又来克看下图。
以上是我对Select()和SelectMany()的理解,如有错误欢迎指正:
参看CSDN的例子。网址:http://msdn.microsoft.com/zh-cn/library/bb546168.aspx#Mtps_DropDownFilterText
标签:set next object pes method make 传递 format ict
原文地址:http://www.cnblogs.com/ioriwellings/p/7135818.html