Many LINQ functions(opens new window) both operate on an IEnumerable<TSource> and also return an IEnumerable<TResult>. The type parameters TSource and TResult may or may not refer to the same type, depending on the method in question and any functions passed to it.
This allows these functions to be fluently chained wiki(opens new window), where one function can act directly on the result of another. This style of code can be used to perform many sequence based operations in a single statement.
For example, it's possible to combine Select, Where and OrderBy to transform, filter and sort a sequence in a single statement.
var someNumbers ={4,3,2,1};var processed = someNumbers
.Select(n => n *2)// Multiply each number by 2.Where(n => n !=6)// Keep all the results, except for 6.OrderBy(n => n);// Sort in ascending order
Any functions that both extend and return the generic IEnumerable<T> type can be used as chained clauses in a single statement. This style of fluent programming is powerful, and should be considered when creating your own extension methods(opens new window).
First, FirstOrDefault, Last, LastOrDefault, Single, and SingleOrDefault
All six methods return a single value of the sequence type, and can be called with or without a predicate.
Depending on the number of elements that match the predicate or, if no predicate is supplied, the number of elements in the source sequence, they behave as follows:
First()
Returns the first element of a sequence, or the first element matching the provided predicate.
If the sequence contains no elements, an InvalidOperationException is thrown with the message: "Sequence contains no elements".
If the sequence contains no elements matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains no matching element".
Returns the first element of a sequence, or the first element matching the provided predicate.
If the sequence contains no elements, or no elements matching the provided predicate, returns the default value of the sequence type using default(T)(opens new window).
Returns the last element of a sequence, or the last element matching the provided predicate.
If the sequence contains no elements, an InvalidOperationException is thrown with the message "Sequence contains no elements."
If the sequence contains no elements matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains no matching element".
If the sequence contains exactly one element, or exactly one element matching the provided predicate, that element is returned.
If the sequence contains no elements, or no elements matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains no elements".
If the sequence contains more than one element, or more than one element matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains more than one element".
Note: in order to evaluate whether the sequence contains exactly one element, at most two elements has to be enumerated.
Example
// Returns "a":new[]{"a"}.Single();// Throws InvalidOperationException because sequence contains more than one element:new[]{"a","b"}.Single();// Returns "b":new[]{"a","b"}.Single(x => x.Equals("b"));// Throws InvalidOperationException:new[]{"a","b"}.Single(x => x.Equals("c"));// Throws InvalidOperationException:newstring[0].Single();// Throws InvalidOperationException because sequence contains more than one element:new[]{"a","a"}.Single();
SingleOrDefault()
If the sequence contains exactly one element, or exactly one element matching the provided predicate, that element is returned.
If the sequence contains no elements, or no elements matching the provided predicate, default(T) is returned.
If the sequence contains more than one element, or more than one element matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains more than one element".
If the sequence contains no elements matching the provided predicate, returns the default value of the sequence type using default(T).
Note: in order to evaluate whether the sequence contains exactly one element, at most two elements has to be enumerated.
Example
// Returns "a":new[]{"a"}.SingleOrDefault();// returns "a"new[]{"a","b"}.SingleOrDefault(x => x =="a");// Returns null:new[]{"a","b"}.SingleOrDefault(x => x =="c");// Throws InvalidOperationException:new[]{"a","a"}.SingleOrDefault(x => x =="a");// Throws InvalidOperationException:new[]{"a","b"}.SingleOrDefault();// Returns null:newstring[0].SingleOrDefault();
Recommendations
Although you can use `FirstOrDefault`, `LastOrDefault` or `SingleOrDefault` to check whether a sequence contains any items, `Any` or `Count` are more reliable. This is because a return value of `default(T)` from one of these three methods doesn't prove that the sequence is empty, as the value of the first / last / single element of the sequence could equally be `default(T)`
Decide on which methods fits your code's purpose the most. For instance, use `Single` only if you must ensure that there is a single item in the collection matching your predicate — otherwise use `First`; as `Single` throw an exception if the sequence has more than one matching element. This of course applies to the "*OrDefault"-counterparts as well.
Regarding efficiency: Although it's often appropriate to ensure that there is only one item (`Single`) or, either only one or zero (`SingleOrDefault`) items, returned by a query, both of these methods require more, and often the entirety, of the collection to be examined to ensure there in no second match to the query. This is unlike the behavior of, for example, the `First` method, which can be satisfied after finding the first match.
Except
The Except method returns the set of items which are contained in the first collection but are not contained in the second. The default IEqualityComparer(opens new window) is used to compare the items within the two sets. There is an overload which accepts an IEqualityComparer(opens new window) as an argument.
Example:
int[] first ={1,2,3,4};int[] second ={0,2,3,5};IEnumerable<int> inFirstButNotInSecond = first.Except(second);// inFirstButNotInSecond = { 1, 4 }
In this case .Except(second) excludes elements contained in the array second, namely 2 and 3 (0 and 5 are not contained in the first array and are skipped).
Note that Except implies Distinct (i.e., it removes repeated elements). For example:
classHoliday:IEquatable<Holiday>{publicstring Name {get;set;}publicboolEquals(Holiday other){return Name == other.Name;}// GetHashCode must return true whenever Equals returns true.publicoverrideintGetHashCode(){//Get hash code for the Name field if it is not null.return Name?.GetHashCode()??0;}}publicclassProgram{publicstaticvoidMain(){List<Holiday> holidayDifference =newList<Holiday>();List<Holiday> remoteHolidays =newList<Holiday>{newHoliday{ Name ="Xmas"},newHoliday{ Name ="Hanukkah"},newHoliday{ Name ="Ramadan"}};List<Holiday> localHolidays =newList<Holiday>{newHoliday{ Name ="Xmas"},newHoliday{ Name ="Ramadan"}};
holidayDifference = remoteHolidays
.Except(localHolidays).ToList();
holidayDifference.ForEach(x => Console.WriteLine(x.Name));}}
The SelectMany linq method 'flattens' an IEnumerable<IEnumerable<T>> into an IEnumerable<T>. All of the T elements within the IEnumerable instances contained in the source IEnumerable will be combined into a single IEnumerable.
var words =new[]{"a,b,c","d,e","f"};var splitAndCombine = words.SelectMany(x => x.Split(','));// returns { "a", "b", "c", "d", "e", "f" }
If you use a selector function which turns input elements into sequences, the result will be the elements of those sequences returned one by one.
Note that, unlike Select(), the number of elements in the output doesn't need to be the same as were in the input.
More real-world example
classSchool{publicStudent[] Students {get;set;}}classStudent{publicstring Name {get;set;}}var schools =new[]{newSchool(){ Students =new[]{newStudent{ Name="Bob"},newStudent{ Name="Jack"}}},newSchool(){ Students =new[]{newStudent{ Name="Jim"},newStudent{ Name="John"}}}};var allStudents = schools.SelectMany(s=> s.Students);foreach(var student in allStudents){
Console.WriteLine(student.Name);}
The SelectMany linq method 'flattens' an IEnumerable<IEnumerable<T>> into an IEnumerable<T>. All of the T elements within the IEnumerable instances contained in the source IEnumerable will be combined into a single IEnumerable.
var words =new[]{"a,b,c","d,e","f"};var splitAndCombine = words.SelectMany(x => x.Split(','));// returns { "a", "b", "c", "d", "e", "f" }
If you use a selector function which turns input elements into sequences, the result will be the elements of those sequences returned one by one.
Note that, unlike Select(), the number of elements in the output doesn't need to be the same as were in the input.
More real-world example
classSchool{publicStudent[] Students {get;set;}}classStudent{publicstring Name {get;set;}}var schools =new[]{newSchool(){ Students =new[]{newStudent{ Name="Bob"},newStudent{ Name="Jack"}}},newSchool(){ Students =new[]{newStudent{ Name="Jim"},newStudent{ Name="John"}}}};var allStudents = schools.SelectMany(s=> s.Students);foreach(var student in allStudents){
Console.WriteLine(student.Name);}
Joins are used to combine different lists or tables holding data via a common key.
Like in SQL, the following kinds of Joins are supported in LINQ: Inner, Left, Right, Cross and Full Outer Joins.
The following two lists are used in the examples below:
var first =newList<string>(){"a","b","c"};// Left datavar second =newList<string>(){"a","c","d"};// Right data
(Inner) Join
var result =from f in first
join s in second on f equals s
selectnew{ f, s };var result = first.Join(second,
f => f,
s => s,(f, s)=>new{ f, s });// Result: {"a","a"}// {"c","c"}
Left outer join
var leftOuterJoin =from f in first
join s in second on f equals s into temp
from t in temp.DefaultIfEmpty()selectnew{ First = f, Second = t};// Or can also do:var leftOuterJoin =from f in first
from s in second.Where(x => x == f).DefaultIfEmpty()selectnew{ First = f, Second = s};// Result: {"a","a"}// {"b", null} // {"c","c"} // Left outer join method syntaxvar leftOuterJoinFluentSyntax = first.GroupJoin(second,
f => f,
s => s,(f, s)=>new{ First = f, Second = s }).SelectMany(temp => temp.Second.DefaultIfEmpty(),(f, s)=>new{ First = f.First, Second = s });
Right Outer Join
var rightOuterJoin =from s in second
join f in first on s equals f into temp
from t in temp.DefaultIfEmpty()selectnew{First=t,Second=s};// Result: {"a","a"}// {"c","c"} // {null,"d"}
Cross Join
var CrossJoin =from f in first
from s in second
selectnew{ f, s };// Result: {"a","a"}// {"a","c"} // {"a","d"} // {"b","a"}// {"b","c"} // {"b","d"} // {"c","a"}// {"c","c"} // {"c","d"}
The examples above have a simple data structure so you can focus on understanding the different LINQ joins technically, but in the real world you would have tables with columns you need to join.
In the following example, there is just one class Region used, in reality you would join two or more different tables which hold the same key (in this example first and second are joined via the common key ID).
// Left datavar first =newList<Region>(){newRegion(1),newRegion(3),newRegion(4)};// Right datavar second =newList<Region>(){newRegion(1,"Eastern"),newRegion(2,"Western"),newRegion(3,"Northern"),newRegion(4,"Southern")};
You can see that in this example first doesn't contain any region descriptions so you want to join them from second. Then the inner join would look like:
// do the inner joinvar result =from f in first
join s in second on f.ID equals s.ID
selectnew{ f.ID, s.RegionDescription };// Result: {1,"Eastern"}// {3, Northern} // {4,"Southern"}
This result has created anonymous objects on the fly, which is fine, but we have already created a proper class - so we can specify it: Instead of select new { f.ID, s.RegionDescription }; we can say select new Region(f.ID, s.RegionDescription);, which will return the same data but will create objects of type Region - that will maintain compatibility with the other objects.
The Skip method returns a collection excluding a number of items from the beginning of the source collection. The number of items excluded is the number given as an argument. If there are less items in the collection than specified in the argument then an empty collection is returned.
The Take method returns a collection containing a number of elements from the beginning of the source collection. The number of items included is the number given as an argument. If there are less items in the collection than specified in the argument then the collection returned will contain the same elements as the source collection.
Warning: LINQ to Entities only supports Skip on ordered queries(opens new window). If you try to use Skip without ordering you will get a NotSupportedException with the message "The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'."
Defining a variable inside a Linq query (let keyword)
In order to define a variable inside a linq expression, you can use the let keyword. This is usually done in order to store the results of intermediate sub-queries, for example:
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var aboveAverages = from number in numbers
let average = numbers.Average()
let nSquared = Math.Pow(number,2)
where nSquared > average
select number;
Console.WriteLine("The average of the numbers is {0}.", numbers.Average());
foreach (int n in aboveAverages)
{
Console.WriteLine("Query result includes number {0} with square of {1}.", n, Math.Pow(n,2));
}
Output:
The average of the numbers is 4.5.
Query result includes number 3 with square of 9.
Query result includes number 4 with square of 16.
Query result includes number 5 with square of 25.
Query result includes number 6 with square of 36.
Query result includes number 7 with square of 49.
Query result includes number 8 with square of 64.
Query result includes number 9 with square of 81.
The Zip extension method acts upon two collections. It pairs each element in the two series together based on position. With a Func instance, we use Zip to handle elements from the two C# collections in pairs. If the series differ in size, the extra elements of the larger series will be ignored.
To take an example from the book "C# in a Nutshell",
int[] numbers ={3,5,7};string[] words ={"three","five","seven","ignored"};IEnumerable<string> zip = numbers.Zip(words,(n, w)=> n +"="+ w);
All is used to check, if all elements of a collection match a condition or not.
see also: .Any(opens new window)
1. Empty parameter
All: is not allowed to be used with empty parameter.
2. Lambda expression as parameter
All: Returns true if all elements of collection satisfies the lambda expression and false otherwise:
var numbers =newList<int>(){1,2,3,4,5};bool result = numbers.All(i => i <10);// truebool result = numbers.All(i => i >=3);// false
3. Empty collection
All: Returns true if the collection is empty and a lambda expression is supplied:
var numbers =newList<int>();bool result = numbers.All(i => i >=0);// true
Note:All will stop iteration of the collection as soon as it finds an element not matching the condition. This means that the collection will not necessarily be fully enumerated; it will only be enumerated far enough to find the first item not matching the condition.
Basics
LINQ is largely beneficial for querying collections (or arrays).
For example, given the following sample data:
var classroom =newClassroom{newStudent{ Name ="Alice", Grade =97, HasSnack =true},newStudent{ Name ="Bob", Grade =82, HasSnack =false},newStudent{ Name ="Jimmy", Grade =71, HasSnack =true},newStudent{ Name ="Greg", Grade =90, HasSnack =false},newStudent{ Name ="Joe", Grade =59, HasSnack =false}}
We can "query" on this data using LINQ syntax. For example, to retrieve all students who have a snack today:
var studentsWithSnacks =from s in classroom.Students
wheres.HasSnack
select s;
Or, to retrieve students with a grade of 90 or above, and only return their names, not the full Student object:
var topStudentNames =from s in classroom.Students
wheres.Grade >=90select s.Name;
The LINQ feature is comprised of two syntaxes that perform the same functions, have nearly identical performance, but are written very differently. The syntax in the example above is called query syntax. The following example, however, illustrates method syntax. The same data will be returned as in the example above, but the way the query is written is different.
var topStudentNames = classroom.Students
.Where(s => s.Grade >=90).Select(s => s.Name);
Aggregate
Aggregate Applies an accumulator function over a sequence.
int[] intList ={1,2,3,4,5,6,7,8,9,10};int sum = intList.Aggregate((prevSum, current)=> prevSum + current);// sum = 55
At the first step prevSum = 1
At the second prevSum = prevSum(at the first step) + 2
At the i-th step prevSum = prevSum(at the (i-1) step) + i-th element of the array
A second overload of Aggregate also receives an seed parameter which is the initial accumulator value. This can be used to calculate multiple conditions on a collection without iterating it more than once.
var result = items.Aggregate(new{ Total =0, Even =0, FourthItems =newList<int>()},(accumelative,item)=>new{
Total = accumelative.Total +1,
Even = accumelative.Even +(item %2==0?1:0),
FourthItems =(accumelative.Total +1)%4==0?newList<int>(accumelative.FourthItems){ item }:
accumelative.FourthItems
});// Result:// Total = 12// Even = 6// FourthItems = [4, 8, 12]
Note that using an anonymous type as the seed one has to instantiate a new object each item because the properties are read only. Using a custom class one can simply assign the information and no new is needed (only when giving the initial seed parameter
Use SelectMany() if you have, or you are creating a sequence of sequences, but you want the result as one long sequence.
In LINQ Query Syntax:
var sequence =from subSequence in sequenceOfSequences
from item in subSequence
select item;
If you have a collection of collections and would like to be able to work on data from parent and child collection at the same time, it is also possible with SelectMany.
Let's define simple classes
publicclassBlogPost{publicint Id {get;set;}publicstring Content {get;set;}publicList<Comment> Comments {get;set;}}publicclassComment{publicint Id {get;set;}publicstring Content {get;set;}}
Let's assume we have following collection.
List<BlogPost> posts =newList<BlogPost>(){newBlogPost(){
Id =1,
Comments =newList<Comment>(){newComment(){
Id =1,
Content ="It's really great!",},newComment(){
Id =2,
Content ="Cool post!"}}},newBlogPost(){
Id =2,
Comments =newList<Comment>(){newComment(){
Id =3,
Content ="I don't think you're right",},newComment(){
Id =4,
Content ="This post is a complete nonsense"}}}};
Now we want to select comments Content along with Id of BlogPost associated with this comment. In order to do so, we can use appropriate SelectMany overload.
To compare a custom data type, we need to implement the IEquatable<T> interface and provide GetHashCode and Equals methods for the type. Or the equality comparer may be overridden:
var foos = collection.OfType<Foo>();// result: IEnumerable<Foo> with item0 and item1var bars = collection.OfType<Bar>();// result: IEnumerable<Bar> item item2 and item3var foosAndBars = collection.OfType<IFoo>();// result: IEnumerable<IFoo> with all four items
Using Where
var foos = collection.Where(item => item isFoo);// result: IEnumerable<IFoo> with item0 and item1var bars = collection.Where(item => item isBar);// result: IEnumerable<IFoo> with item2 and item3
Using Cast
var bars = collection.Cast<Bar>();// throws InvalidCastException on the 1st itemvar foos = collection.Cast<Foo>();// throws InvalidCastException on the 3rd itemvar foosAndBars = collection.Cast<IFoo>();// OK
GroupBy
GroupBy is an easy way to sort a IEnumerable<T> collection of items into distinct groups.
Simple Example
In this first example, we end up with two groups, odd and even items.
List<int> iList =newList<int>(){1,2,3,4,5,6,7,8,9};var grouped = iList.GroupBy(x => x %2==0);//Groups iList into odd [13579] and even[2468] items foreach(vargroupin grouped){foreach(int item ingroup){
Console.Write(item);// 135792468 (first odd then even)}}
More Complex Example
Let's take grouping a list of people by age as an example.
First, we'll create a Person object which has two properties, Name and Age.
publicclassPerson{publicint Age {get;set;}publicstring Name {get;set;}}
Then we create our sample list of people with various names and ages.
List<Person> people =newList<Person>();
people.Add(newPerson{Age =20, Name ="Mouse"});
people.Add(newPerson{Age =30, Name ="Neo"});
people.Add(newPerson{Age =40, Name ="Morpheus"});
people.Add(newPerson{Age =30, Name ="Trinity"});
people.Add(newPerson{Age =40, Name ="Dozer"});
people.Add(newPerson{Age =40, Name ="Smith"});
Then we create a LINQ query to group our list of people by age.
var query = people.GroupBy(x => x.Age);
Doing so, we can see the Age for each group, and have a list of each person in the group.
foreach(var result in query){
Console.WriteLine(result.Key);foreach(var person in result)
Console.WriteLine(person.Name);}
The IEnumerable interface is the base interface for all generic enumerators and is a quintessential part of understanding LINQ. At its core, it represents the sequence.
In addition to representing the sequence, any class that inherits from IEnumerable must provide an IEnumerator. The enumerator exposes the iterator for the enumerable, and these two interconnected interfaces and ideas are the source of the saying "enumerate the enumerable".
"Enumerating the enumerable" is an important phrase. The enumerable is simply a structure for how to iterate, it does not hold any materialized objects. For example, when sorting, an enumerable may hold the criteria of the field to sort, but using .OrderBy() in itself will return an IEnumerable which only knows how to sort. Using a call which will materialize the objects, as in iterate the set, is known as enumerating (for example .ToList()). The enumeration process will use the the enumerable definition of how in order to move through the series and return the relevant objects (in order, filtered, projected, etc.).
Only once the enumerable has been enumerated does it cause the materialization of the objects, which is when metrics like time complexity(opens new window) (how long it should take related to series size) and spacial complexity (how much space it should use related to series size) can be measured.
Creating your own class that inherits from IEnumerable can be a little complicated depending on the underlying series that needs to be enumerable. In general it is best to use one of the existing generic collections. That said, it is also possible to inherit from the IEnumerable interface without having a defined array as the underlying structure.
For example, using the Fibonacci series as the underlying sequence. Note that the call to Where simply builds an IEnumerable, and it is not until a call to enumerate that enumerable is made that any of the values are materialized.
voidMain(){Fibonacci Fibo =newFibonacci();IEnumerable<long> quadrillionplus = Fibo.Where(i => i >1000000000000);
Console.WriteLine("Enumerable built");
Console.WriteLine(quadrillionplus.Take(2).Sum());
Console.WriteLine(quadrillionplus.Skip(2).First());IEnumerable<long> fibMod612 = Fibo.OrderBy(i => i %612);
Console.WriteLine("Enumerable built");
Console.WriteLine(fibMod612.First());//smallest divisible by 612}publicclassFibonacci:IEnumerable<long>{privateint max =90;//Enumerator called typically from foreachpublicIEnumeratorGetEnumerator(){long n0 =1;long n1 =1;
Console.WriteLine("Enumerating the Enumerable");for(int i =0; i < max; i++){yieldreturn n0+n1;
n1 += n0;
n0 = n1-n0;}}//Enumerable called typically from linqIEnumerator<long> IEnumerable<long>.GetEnumerator(){long n0 =1;long n1 =1;
Console.WriteLine("Enumerating the Enumerable");for(int i =0; i < max; i++){yieldreturn n0+n1;
n1 += n0;
n0 = n1-n0;}}}
Output
Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352
The strength in the second set (the fibMod612) is that even though we made the call to order our entire set of Fibonacci numbers, since only one value was taken using .First() the time complexity was O(n) as only 1 value needed to be compared during the ordering algorithm's execution. This is because our enumerator only asked for 1 value, and so the entire enumerable did not have to be materialized. Had we used .Take(5) instead of .First() the enumerator would have asked for 5 values, and at most 5 values would need to be materialized. Compared to needing to order an entire set and then take the first 5 values, the principle of saves a lot of execution time and space.
Where
Returns a subset of items which the specified predicate is true for them.
List<string> trees =newList<string>{"Oak","Birch","Beech","Elm","Hazel","Maple"};
Method syntax
// Select all trees with name of length 3var shortTrees = trees.Where(tree => tree.Length ==3);// Oak, Elm
Query syntax
var shortTrees =from tree in trees
wheretree.Length ==3select tree;// Oak, Elm
Using Range with various Linq methods
You can use the Enumerable class alongside Linq queries to convert for loops into Linq one liners.
Select Example
Opposed to doing this:
var asciiCharacters =newList<char>();for(var x =0; x <256; x++){
asciiCharacters.Add((char)x);}
You can do this:
var asciiCharacters = Enumerable.Range(0,256).Select(a =>(char) a);
Where Example
In this example, 100 numbers will be generated and even ones will be extracted
var evenNumbers = Enumerable.Range(1,100).Where(a => a %2==0);
Using SelectMany instead of nested loops
Given 2 lists
var list1 =newList<string>{"a","b","c"};var list2 =newList<string>{"1","2","3","4"};
if you want to output all permutations you could use nested loops like
var result =newList<string>();foreach(var s1 in list1)foreach(var s2 in list2)
result.Add($"{s1}{s2}");
Using SelectMany you can do the same operation as
var result = list1.SelectMany(x => list2.Select(y =>$"{x}{y}", x, y)).ToList();
Contains
MSDN:
Determines whether a sequence contains a specified element by using a
specified `IEqualityComparer`
List<int> numbers =newList<int>{1,2,3,4,5};var result1 = numbers.Contains(4);// truevar result2 = numbers.Contains(8);// falseList<int> secondNumberCollection =newList<int>{4,5,6,7};// Note that can use the Intersect method in this casevar result3 = secondNumberCollection.Where(item => numbers.Contains(item));// will be true only for 4,5
Using a user defined object:
publicclassPerson{publicstring Name {get;set;}}List<Person> objects =newList<Person>{newPerson{ Name ="Nikki"},newPerson{ Name ="Gilad"},newPerson{ Name ="Phil"},newPerson{ Name ="John"}};//Using the Person's Equals method - override Equals() and GetHashCode() - otherwise it//will compare by reference and result will be falsevar result4 = objects.Contains(newPerson{ Name ="Phil"});// true
Using the Enumerable.Contains(value, comparer) overload:
In this example, the single argument passed to ToDictionary is of type Func<TSource, TKey>, which returns the key for each element.
This is a concise way to perform the following operation:
Dictionary<int, User> usersById =newDictionary<int User>();foreach(User u in users){
usersById.Add(u.Id, u);}
You can also pass a second parameter to the ToDictionary method, which is of type Func<TSource, TElement> and returns the Value to be added for each entry.
It is also possible to specify the IComparer that is used to compare key values. This can be useful when the key is a string and you want it to match case-insensitive.
Note: the ToDictionary method requires all keys to be unique, there must be no duplicate keys. If there are, then an exception is thrown: ArgumentException: An item with the same key has already been added. If you have a scenario where you know that you will have multiple elements with the same key, then you are better off using ToLookup(opens new window) instead.
SkipWhile
SkipWhile() is used to exclude elements until first non-match (this might be counter intuitive to most)
int[] list ={42,42,6,6,6,42};var result = list.SkipWhile(i => i ==42);// Result: 6, 6, 6, 42
DefaultIfEmpty
DefaultIfEmpty is used to return a Default Element if the Sequence contains no elements. This Element can be the Default of the Type or a user defined instance of that Type. Example:
With DefaultIfEmpty the traditional Linq Join can return a default object if no match was found. Thus acting as a SQL's Left Join. Example:
var leftSequence =newList<int>(){99,100,5,20,102,105};var rightSequence =newList<char>(){'a','b','c','i','d'};var numbersAsChars =from l in leftSequence
join r in rightSequence
onl equals (int)r into leftJoin
from result in leftJoin.DefaultIfEmpty('?')selectnew{
Number = l,
Character = result
};foreach(var item in numbersAsChars){
Console.WriteLine("Num = {0} ** Char = {1}", item.Number, item.Character);}
ouput:
Num =99 Char =c
Num =100 Char =d
Num =5 Char =?
Num =20 Char =?
Num =102 Char =?
Num =105 Char = i
In the case where a DefaultIfEmpty is used (without specifying a default value) and that will result will no matching items on the right sequence one must make sure that the object is not null before accessing its properties. Otherwise it will result in a NullReferenceException. Example:
var leftSequence =newList<int>{1,2,5};var rightSequence =newList<dynamic>(){new{ Value =1},new{ Value =2},new{ Value =3},new{ Value =4},};var numbersAsChars =(from l in leftSequence
join r in rightSequence
on l equals r.Value into leftJoin
from result in leftJoin.DefaultIfEmpty()selectnew{
Left = l,// 5 will not have a matching object in the right so result // will be equal to null. // To avoid an error use:// - C# 6.0 or above - ?. // - Under - result == null ? 0 : result.Value
Right = result?.Value
}).ToList();
SequenceEqual
SequenceEqual is used to compare two IEnumerable<T> sequences with each other.
int[] a =newint[]{1,2,3};int[] b =newint[]{1,2,3};int[] c =newint[]{1,3,2};bool returnsTrue = a.SequenceEqual(b);bool returnsFalse = a.SequenceEqual(c);
ElementAt and ElementAtOrDefault
ElementAt will return the item at index n. If n is not within the range of the enumerable, throws an ArgumentOutOfRangeException.
Both ElementAt and ElementAtOrDefault are optimized for when the source is an IList<T> and normal indexing will be used in those cases.
Note that for ElementAt, if the provided index is greater than the size of the IList<T>, the list should (but is technically not guaranteed to) throw an ArgumentOutOfRangeException.
Joining multiple sequences
Consider entities Customer, Purchase and PurchaseItem as follows:
publicclassCustomer{publicstring Id {get;set}// A unique Id that identifies customer publicstring Name {get;set;}}publicclassPurchase{publicstring Id {get;set}publicstring CustomerId {get;set;}publicstring Description {get;set;}}publicclassPurchaseItem{publicstring Id {get;set}publicstring PurchaseId {get;set;}publicstring Detail {get;set;}}
Consider following sample data for above entities:
var customers =newList<Customer>(){newCustomer(){
Id = Guid.NewGuid().ToString(),
Name ="Customer1"},newCustomer(){
Id = Guid.NewGuid().ToString(),
Name ="Customer2"}};var purchases =newList<Purchase>(){newPurchase(){
Id = Guid.NewGuid().ToString(),
CustomerId = customers[0].Id,
Description ="Customer1-Purchase1"},newPurchase(){
Id = Guid.NewGuid().ToString(),
CustomerId = customers[0].Id,
Description ="Customer1-Purchase2"},newPurchase(){
Id = Guid.NewGuid().ToString(),
CustomerId = customers[1].Id,
Description ="Customer2-Purchase1"},newPurchase(){
Id = Guid.NewGuid().ToString(),
CustomerId = customers[1].Id,
Description ="Customer2-Purchase2"}};var purchaseItems =newList<PurchaseItem>(){newPurchaseItem(){
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[0].Id,
Detail ="Purchase1-PurchaseItem1"},newPurchaseItem(){
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[1].Id,
Detail ="Purchase2-PurchaseItem1"},newPurchaseItem(){
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[1].Id,
Detail ="Purchase2-PurchaseItem2"},newPurchaseItem(){
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[3].Id,
Detail ="Purchase3-PurchaseItem1"}};
Now, consider below linq query:
var result =from c in customers
join p in purchases on c.Id equals p.CustomerId // first joinjoin pi in purchaseItems on p.Id equals pi.PurchaseId // second joinselectnew{
c.Name, p.Description, pi.Detail
};
To output the result of above query:
foreach(var resultItem in result){
Console.WriteLine($"{resultItem.Name}, {resultItem.Description}, {resultItem.Detail}");}
PropertyInfo[] stringProps =typeof(string).GetProperties();//string propertiesPropertyInfo[] builderProps =typeof(StringBuilder).GetProperties();//stringbuilder propertiesvar query =from s in stringProps
join b in builderProps
onnew{ s.Name, s.PropertyType } equals new{ b.Name, b.PropertyType }selectnew{
s.Name,
s.PropertyType,
StringToken = s.MetadataToken,
StringBuilderToken = b.MetadataToken
};
Note that anonymous types in above join must contain same properties since objects are considered equal only if all their properties are equal. Otherwise query won't compile.
Sum
The Enumerable.Sum extension method calculates the sum of numeric values.
In case the collection's elements are themselves numbers, you can calculate the sum directly.
In case the type of the elements is a complex type, you can use a lambda expression to specify the value that should be calculated:
var totalMonthlySalary = employees.Sum( employee => employee.MonthlySalary );
Sum extension method can calculate with the following types:
Int32
Int64
Single
Double
Decimal
In case your collection contains nullable types, you can use the null-coalescing operator to set a default value for null elements:
int?[] numbers =newint?[]{1,null,6};
Console.WriteLine( numbers.Sum( number => number ??0));//outputs 7
ToLookup
ToLookup returns a data structure that allows indexing. It is an
extension method. It produces an ILookup instance that can be indexed
or enumerated using a foreach-loop. The entries are combined into
groupings at each key. - dotnetperls
string[] array ={"one","two","three"};//create lookup using string length as keyvar lookup = array.ToLookup(item => item.Length);//join the values whose lengths are 3
Console.WriteLine(string.Join(",",lookup[3]));//output: one,two
Another Example:
int[] array ={1,2,3,4,5,6,7,8};//generate lookup for odd even numbers (keys will be 0 and 1)var lookup = array.ToLookup(item => item %2);//print even numbers after joining
Console.WriteLine(string.Join(",",lookup[0]));//output: 2,4,6,8//print odd numbers after joining
Console.WriteLine(string.Join(",",lookup[1]));//output: 1,3,5,7
When the value is an integer, double or float it starts with the minimum value, which means that you get first the negative values, than zero and afterwords the positive values (see Example 1).
When you order by a char the method compares the ascii values of the chars to sort the collection (see Example 2).
When you sort strings the OrderBy method compares them by taking a look at their CultureInfo(opens new window) but normaly starting with the first letter in the alphabet (a,b,c...).
This kind of order is called ascending, if you want it the other way round you need descending (see OrderByDescending).
classPerson{publicstring Name {get;set;}publicint Age {get;set;}}var people =new[]{newPerson{Name ="Alice", Age =25},newPerson{Name ="Bob", Age =21},newPerson{Name ="Carol", Age =43}};var youngestPerson = people.OrderBy(x => x.Age).First();var name = youngestPerson.Name;// Bob
Select - Transforming elements
Select allows you to apply a transformation to every element in any data structure implementing IEnumerable.
Getting the first character of each string in the following list:
List<String> trees =newList<String>{"Oak","Birch","Beech","Elm","Hazel","Maple"};
Using regular (lambda) syntax
//The below select stament transforms each element in tree into its first character.IEnumerable<String> initials = trees.Select(tree => tree.Substring(0,1));foreach(String initial in initials){
System.Console.WriteLine(initial);}
Count returns the number of elements in an IEnumerable<T>. Count also exposes an optional predicate parameter that allows you to filter the elements you want to count.
int[] array ={1,2,3,4,2,5,3,1,2};int n = array.Count();// returns the number of elements in the arrayint x = array.Count(i => i >2);// returns the number of elements in the array greater than 2
LongCount works the same way as Count but has a return type of long and is used for counting IEnumerable<T> sequences that are longer than int.MaxValue
int[] array =GetLargeArray();long n = array.LongCount();// returns the number of elements in the arraylong x = array.LongCount(i => i >100);// returns the number of elements in the array greater than 100
Incrementally building a query
Because LINQ uses deferred execution, we can have a query object that doesn't actually contain the values, but will return the values when evaluated. We can thus dynamically build the query based on our control flow, and evaluate it once we are finished:
IEnumerable<VehicleModel>BuildQuery(int vehicleType,SearchModel search,int start =1,int count =-1){IEnumerable<VehicleModel> query = _entities.Vehicles
.Where(x => x.Active && x.Type == vehicleType).Select(x =>newVehicleModel{
Id = v.Id,
Year = v.Year,
Class = v.Class,
Make = v.Make,
Model = v.Model,
Cylinders = v.Cylinders ??0});
Once we have the query object, we can evaluate the results with a foreach loop, or one of the LINQ methods that returns a set of values, such as ToList or ToArray:
SearchModel sm;// populate the search model here// ...List<VehicleModel> list =BuildQuery(5, sm).ToList();
GroupJoin with outer range variable
Customer[] customers = Customers.ToArray();Purchase[] purchases = Purchases.ToArray();var groupJoinQuery =from c in customers
join p in purchases on c.ID equals p.CustomerID
into custPurchases
selectnew{
CustName = c.Name,
custPurchases
};
Linq Quantifiers
Quantifier operations return a Boolean value if some or all of the elements in a sequence satisfy a condition. In this article, we will see some common LINQ to Objects scenarios where we can use these operators.
There are 3 Quantifiers operations that can be used in LINQ:
All – used to determine whether all the elements in a sequence satisfy a condition.
Eg:
int[] array ={10,20,30};// Are all elements >= 10? YES
array.All(element => element >=10);// Are all elements >= 20? NO
array.All(element => element >=20);// Are all elements < 40? YES
array.All(element => element <40);
Any - used to determine whether any elements in a sequence satisfy a condition.
Eg:
int[] query=newint[]{2,3,4}
query.Any (n => n ==3);
Contains - used to determine whether a sequence contains a specified element.
Eg:
//for int arrayint[] query =newint[]{1,2,3};
query.Contains(1);//for string arraystring[] query={"Tom","grey"};
query.Contains("Tom");//for a stringvar stringValue="hello";
stringValue.Contains("h");
TakeWhile
TakeWhile returns elements from a sequence as long as the condition is true
int[] list ={1,10,40,50,44,70,4};var result = list.TakeWhile(item => item <50).ToList();// result = { 1, 10, 40 }
Build your own Linq operators for IEnumerable
One of the great things about Linq is that it is so easy to extend. You just need to create an extension method(opens new window) whose argument is IEnumerable<T>.
This example splits the items in an IEnumerable<T> into lists of a fixed size, the last list containing the remainder of the items. Notice how the object to which the extension method is applied is passed in (argument source) as the initial argument using the this keyword. Then the yield keyword is used to output the next item in the output IEnumerable<T> before continuing with execution from that point (see yield keyword(opens new window)).
This example would be used in your code like this:
//using MyNamespace;var items =newList<int>{2,3,4,5,6};foreach(List<int> sublist in items.Batch(3)){// do something}
On the first loop, sublist would be {2, 3, 4} and on the second {5, 6}.
Custom LinQ methods can be combined with standard LinQ methods too. e.g.:
//using MyNamespace;var result = Enumerable.Range(0,13)// generate a list.Where(x => x%2==0)// filter the list or do something other.Batch(3)// call our extension method.ToList()// call other standard methods
This query will return even numbers grouped in batches with a size of 3: {0, 2, 4}, {6, 8, 10}, {12}
Remember you need a using MyNamespace; line in order to be able to access the extension method.
Reverse
Inverts the order of the elements in a sequence.
If there is no items throws a ArgumentNullException: source is null.
Example:
// Create an array.int[] array ={1,2,3,4};//Output:// Call reverse extension method on the array. //4var reverse = array.Reverse();//3// Write contents of array to screen. //2foreach(intvaluein reverse)//1
Console.WriteLine(value);
Remeber that Reverse() may work diffrent depending on the chain order of your LINQ statements.
//Create List of charsList<int> integerlist =newList<int>(){1,2,3,4,5,6};//Reversing the list then taking the two first elementsIEnumerable<int> reverseFirst = integerlist.Reverse<int>().Take(2);//Taking 2 elements and then reversing only thos twoIEnumerable<int> reverseLast = integerlist.Take(2).Reverse();//reverseFirst output: 6, 5//reverseLast output: 2, 1
This method can encounter problems if u mutate the list while iterating.
OrderByDescending
Orders a collection by a specified value.
When the value is an integer, double or float it starts with the maximal value, which means that you get first the positive values, than zero and afterwords the negative values (see Example 1).
When you order by a char the method compares the ascii values of the chars to sort the collection (see Example 2).
When you sort strings the OrderBy method compares them by taking a look at their CultureInfo(opens new window) but normaly starting with the last letter in the alphabet (z,y,x,...).
This kind of order is called descending, if you want it the other way round you need ascending (see OrderBy).
classPerson{publicstring Name {get;set;}publicint Age {get;set;}}var people =new[]{newPerson{Name ="Alice", Age =25},newPerson{Name ="Bob", Age =21},newPerson{Name ="Carol", Age =43}};var oldestPerson = people.OrderByDescending(x => x.Age).First();var name = oldestPerson.Name;// Carol
Concat
Merges two collections (without removing duplicates)
List<int> foo =newList<int>{1,2,3};List<int> bar =newList<int>{3,4,5};// Through Enumerable static classvar result = Enumerable.Concat(foo, bar).ToList();// 1,2,3,3,4,5// Through extension methodvar result = foo.Concat(bar).ToList();// 1,2,3,3,4,5
Select with Func<TSource, int, TResult> selector - Use to get ranking of elements
On of the overloads of the Select extension methods also passes the index of the current item in the collection being selected. These are a few uses of it.
Get the "row number" of the items
var rowNumbers = collection.OrderBy(item => item.Property1).ThenBy(item => item.Property2).ThenByDescending(item => item.Property3).Select((item, index)=>new{ Item = item, RowNumber = index }).ToList();
Get the rank of an item within its group
var rankInGroup = collection.GroupBy(item => item.Property1).OrderBy(group=>group.Key).SelectMany(group=>group.OrderBy(item => item.Property2).ThenByDescending(item => item.Property3).Select((item, index)=>new{
Item = item,
RankInGroup = index
})).ToList();
Get the ranking of groups (also known in Oracle as dense_rank)
var rankOfBelongingGroup = collection.GroupBy(item => item.Property1).OrderBy(group=>group.Key).Select((group, index)=>new{
Items =group,
Rank = index
}).SelectMany(v => v.Items,(s, i)=>new{
Item = i,
DenseRank = s.Rank
}).ToList();