Standard query operators provide querying capabilities on objects in the .NET Framework. It is a set of methods which can operate on objects whose type implements the IEnumerable<T>
or IQueryable<T>
interface. IEnumerable
exposes the enumerator which iterates over a collection of a specified type. There are different sets of methods that operate on static members of Enumerable
and Queryable
classes. Each query can have any number of methods within it. The more number of methods in a query, the more complex it is. The query operators are useable with any .NET language that support generics.
There are some differences in the query execution timings, depending on the value that the query returns. If the query returns a single value, like an average or a sum, it executes immediately and returns the value. If it is a sequence of values, the query would be deferred and would return an enumerable object.
In this chapter we will see types of standard query operators provided by LINQ and how we can use some of those against different data sources.
Whenever we create a new project, we get default namespaces added to the project. The namespace that takes care of importing the query operators is:
using System.Linq;
We shall see examples of some of these query operators that are used in many applications. Before going into the details of operators, we have to define the classes and objects on which we can apply the queries. We will create the following classes in our project.
public class Categories { public string Category {Get; Set;} public string Description {Get; Set;} } public class Item { public string Category {Get; Set;} public string Name {Get; Set;} public string Ingredients {Get; Set;} public string TotalFat {Get; Set;} public string Cholesterol {Get; Set;} public string TotalCarbohydrates {Get; Set;} public string Protein {Get; Set;} public double Price {Get; Set;} public FatContent FatContents {Get; Set;} } public class FatContent { public string SaturatedFat {Get; Set;} public string TransFat {Get; Set;} public string OtherFat {Get; Set;} }
The Categories
class holds different categories of items like ice-creams and pastries. The Items
class contains the properties that hold information about different catagories. The third class, FatContent
, holds detailed information about fat content in each item.
Following is a table that lists operators provided by LINQ:
Operators |
Description |
---|---|
Aggregation operators |
Aggregation operators are used to compute a single value from a collection of values. For example, getting the average or sum of numbers from the collection. |
Projection operators |
Projection operators are useful for transforming elements. |
Concatenation operators |
This operator performs the operation of concatenating one sequence to another. |
Element operators |
Element operators return a single element from a sequence of elements. For example, returning the first, last, or an element at a specific index from a list. |
Conversion operators |
These operators change the type of the input object. |
Equality operators |
These operators check for equality of two sequences. For example, two sequences having the same number of elements are considered equal. |
Generation operators |
These operators are used for generating a new sequence of values. |
Grouping operators |
These operators are for grouping elements together that share a common attribute. |
Join operators |
These operators are used to associate objects from one data source with objects in another data source, based on a common attribute. |
Partitioning operators |
These operators are used to divide an input sequence into two or more sections, and then return the one section that is required. |
Quantifiers |
These operators perform the operation of checking whether some or all of the elements in a sequence satisfy a condition. |
Restriction operators |
These operators restrict the query result to contain elements that satisfy the specific condition. |
Set operators |
This is to get the result sets based on the presence or absence of equivalent elements in the same or another collection. |
Ordering operators |
These operators are used for ordering elements in a sequence based on one or more attributes. We can also specify the order within the group of elements to be sorted. |
Filtering is an operation to restrict the result of a query to contain elements that satisfy a specific condition. We will cover restriction operators in detail in the following sub-sections.
The Where
operator filters a sequence, and the declaration would be as follows:
For IEnumerable
elements:
public static IEnumerable<TSource> Where<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
For IQueryable
elements:
public static IQueryable<TResult> OfType<TResult> ( IQueryable source )
The Where
operator returns an enumerable object from the arguments passed in. When the returned object is enumerated, the enumeration takes places on the sequence and returns those elements for which the predicate function returns true. In the above declaration, the first argument is the source to be tested and the second argument is optional, and if present it represents the elements in the source.
The following example returns the items with price less than 10.
IEnumerable<Item> lowPricedItems = from item in items where item.Price < 10 select item;
An equivalent translation for the previous query, is given as follows:
IEnumerable<Item> itemsWithLessPrice = items. Where(I => I.Price < 10);
The following example shows the usage of the IQueryable
method to filter elements in a sequence.
IQueryable<Item> qryItemsWithLessPrice = items.AsQueryable(). Where(I => I.Price < 10);
If the source or predicate is null in the Where
clause, then an ArgumentNullException
will be thrown.
This operator filters elements based on the type of elements in the collection. Following is a list that contains different objects, like String
and Icecreams
, within the same ArrayList
:
private static ArrayList GetStringsandIcecreams() { System.Collections.ArrayList arrList = new System.Collections.ArrayList(4); arrList.Add("String value One"); arrList.Add("String value Two"); arrList.Add("String value Three"); arrList.Add(new Icecreams {Category="Icecreams", Name="Chocolate Fudge Icecream", Ingredients="cream, milk, mono and diglycerides...", Cholesterol="50mg", Protein="4g", TotalCarbohydrates="35g", TotalFat="20g", Price=10.5 }); arrList.Add(new Icecreams {Category="Icecreams", Name="Vanilla Icecream", Ingredients="vanilla extract, guar gum, cream...", Cholesterol="65mg", Protein="4g", TotalCarbohydrates="26g", TotalFat="16g", Price=9.80}); return arrList; }
Now from this list, if we want to get a list of strings, we can use the OfType
operator to filter the objects.
ArrayList arrList = GetStringsandIcecreams(); // Apply OfType() to the ArrayList. IEnumerable<string> query1 = arrList.OfType<string>(); Console.WriteLine("Elements of type 'string' are:"); foreach (string str in query1) Console.WriteLine(str);
If we want to extract the objects of type Icecream
from the list, we filter for the Icecream
object.
// Call the type OfType() and then the Where() operator // to filter the types from the list with a condition IEnumerable<Icecreams> query2 = arrList.OfType<Icecreams>().Where(icecrms => icecrms.Name.Contains("Vanilla Icecream")); Console.WriteLine(" The Icecream object that contains the name 'Vanilla Icecream':"); foreach (Icecreams ice in query2) Console.WriteLine(ice.Name);
These operators are used for transforming one form of elements into another. For example, we can project one or two properties of an object to create a new type. We can also project the original object without any change. We will cover projection operators in detail in the following sub-sections:
This select
operator is implemented using deferred execution. This query gets executed only when the return object in the query is enumerated using looping statements or by calling the enumeration methods. The enumeration happens by calling the GetEnumerator
method, which is called implicitly when using the foreach
loop. Shown below are the syntaxes for using the select
operator.
public static IEnumerable<TResult> Select<TSource, TResult> ( IEnumerable<TSource> source, Func<TSource, TResult> selector ) public static IQueryable<TResult> Select<TSource, TResult> ( IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector )
The first argument is the element to process and the second argument represents the index of the element in the source of the first argument. The element returned from the selector method could be an object or a collection. If it is a collection, the programmer has to take care of reading the collection and returning the values.
The following example creates a sequence of the names of all items:
IEnumerable<string> icecreamNames = items.Select(itm => itm.Name);
Following is an equivalent query of the above expression:
IEnumerable<string> icecreamsNames = from itm in items select itm.Name;
The following code returns items with a price less than 10:
IEnumerable<Item> lowPricedItems = from item in items where item.Price < 10 select item; Console.WriteLine("Items with low price:"); foreach (var item in lowPricedItems) { Console.WriteLine("Price of {0} is {1} ", item.Name, item.Price); }
The following expression is another example to retrieve items and their prices where the price is less than 10:
var IcecreamsPrices = items.Where(itm => itm.Price < 10) .Select(itm => new { itm.Name, itm.Price }) .ToList(); foreach (var ices in IcecreamsPrices) { Console.WriteLine("The price of {0} is {1}", ices. Name, ices.Price); }
If the source or the selector in the above methods is null then exception of type ArgumentNullException
will be thrown.
The SelectMany
operator performs a one-to-many projection on sequences. This operator enumerates the source and maps each element to an enumerable object. It also enumerates these enumerable objects, and retrieves elements. The first argument is the source element to process, and if the second element is preset, then it represents the elements within the source sequence.
The syntaxes for using the SelectMany
operator are as follows:
public static IEnumerable<TResult> SelectMany<TSource, TResult> ( IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector ) public static IQueryable<TResult> SelectMany<TSource, TResult> ( IQueryable<TSource> source, Expression<Func<TSource, IEnumerable<TResult>>> selector )
The following code example shows how to use SelectMany
to perform a one-to-many projection. Let us define a new item object with properties including a list, which contains the list of ingredients for the item.
public class NewItem { public string Category { get; set; } public string Name { get; set; } public List<string> Ingredients { get; set; } public double Price { get; set; } }
Now define a method to create a list of items using the new object.
private static List<NewItem> GetNewItemsList() { List<NewItem> itemsList = new List<NewItem> { new NewItem { Category="Icecreams", Name="Chocolate Fudge Icecream", Ingredients = new List<string> {"cream", "milk", "mono and diglycerides"}, Price=10.5 }, new NewItem { Category="Icecreams", Name="Vanilla Icecream", Ingredients= new List<string> {"vanilla extract", "guar gum", "cream"}, Price=9.80 }, new NewItem { Category="Icecreams", Name="Banana Split Icecream", Ingredients= new List<string> {"Banana", "guar gum", "cream"}, Price=7.5 } }; return itemsList; }
In the above method you can see a list of strings, Ingredients
, within the itemsList
. Now using the SelectMany
operator, collect all the distinct Ingredients
required for all items.
List<NewItem> itemss = GetNewItemsList(); IEnumerable<string> ingredients = itemss. SelectMany(ing => ing.Ingredients); Console.WriteLine("List of all Ingredients for the Icecreams"); foreach (string str in ingredients.Distinct()) { Console.WriteLine(str); }
The output of this code would be a collection of ingredients for each item.
A join is an association of objects from different data sources that share a common attribute. These operators perform the same operations that are performed by the database queries. Each data source will have certain key attributes by which we can compare the values, and collect information. The different join operators are covered in detail in the following sub-sections.
This operator joins two sequences, based on matching keys extracted from elements in sequences.
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult> ( IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector ) public static IQueryable<TResult> Join<TOuter, TInner, TKey, TResult> ( IQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector )
The IEqualityComparer
is used to compare keys. This is shown in the following code:
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult> ( IEnumerable<TOuter> outer, Enumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer ) public static IQueryable<TResult> Join<TOuter, TInner, TKey, TResult> ( IQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector, IEqualityComparer<TKey> comparer )
This is similar to inner join in relation database terms. These operators join two different sequences and collect common information from the sequences with the help of matching keys in the sequences. The outerKeySelector
and innerKeySelector
arguments specify functions that extract the join key values from elements of the outer and inner sequences, respectively. The resultSelector
argument specifies a function that creates a result element from two matching outer and inner sequence elements. It first enumerates the inner sequence and collects the elements and their keys using innerKeySelector
. It then enumerates the outer sequence to collect the elements and their keys using the outerKeySelector
function. Using these selections, the resultSelector
is evaluated for the resulting sequence. It also maintains the order of the elements in sequences.
The following code is an example for joining categories
with items
having category
as the key between these two sequences. The result is a combination of categories, items, and ingredients.
List<Item> items = GetItemsList(); List<Categories> categories = GetCategoriesList(); var CategoryItems = categories.Join(items, category => category.Category, item => item.Category, (category, item) => new { Category = category.Category, Item = item.Name, Ingr = item.Ingredients }); foreach (var str in CategoryItems) { Console.WriteLine("{0} - {1} - {2}", str.Category, str.Item, str.Ingr); }
Following is an equivalent query for the example above:
List<Item> items = GetItemsList(); List<Categories> categories = GetCategoriesList(); var CategoryItemJoin = from Cat in categories join Itm in items on Cat.Category equals Itm.Category select new { Cat.Category, Itm.Name, Itm.Ingredients }; Console.WriteLine("Join using Query :"); foreach (var elements in CategoryItemJoin) { Console.WriteLine("{0} - {1} - {2}", elements.Category, elements.Name, elements.Ingredients); }
An ArgumentNullException
is thrown if any of the argument is null.
The GroupJoin
operator groups the results of a join between two sequences based on equality of keys. The default equality comparer is used to compare keys.
public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult> ( 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> ( IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector, IEqualityComparer<TKey> comparer )
All the arguments are very similar to those we saw under the Join
operator. The resultSelector
function is called only once for each outer element together with a collection of all the inner elements that match the outer elements. This differs from the Join
operator, in which the result selector function is invoked on pairs that contain one element from outer and one element from inner. This is similar to the inner joins and left outer joins in relational database terms.
The following code is an example of grouping items under various categories:
List<Item> items = GetItemsList(); List<Categories> categories = GetCategoriesList(); var categoriesAndItems = categories.GroupJoin(items, category => category.Category, item => item.Category, (category, categoryItems) => new { Category = category.Category, Items = categoryItems.Select(item => item.Name) }); foreach (var value in categoriesAndItems) { Console.WriteLine("{0} : ", value.Category); foreach (string nam in value.Items) { Console.WriteLine(" {0} ", nam); } }
The same group join can also be achieved through the following query. In this case we are grouping the items according to the category and get the total price of all the items under that category. The keyword, into
, is used here for grouping the result.
List<Item> items = GetItemsList(); List<Categories> categories = GetCategoriesList(); var CategoryItemgroup = from Cat in categories join Itm in items on Cat.Category equals Itm.Category into CustItem select new { Cat.Category, TotalPrice=CustItem.Sum(prc => prc.Price)}; Console.WriteLine("GroupJoin using Query :"); foreach (var elements in CategoryItemgroup) { Console.WriteLine("{0} - {1} ", elements.Category, elements.TotalPrice); }
An ArgumentNullException
is thrown if any argument is null.
Concatenation is the operation of appending one sequence to another. Concat
is the operator used for concatenating.
Concatenation operator combines two different collections into one. When the returned object is enumerated, it first enumerates the first sequence yielding the elements and then it enumerates the second sequence and yields the elements in it. Following is the declaration syntax of this operator:
public static IEnumerable<TSource> Concat<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second )
Following is the query which uses the concatenating operator to concatenate an item's name, ingredients and price.
List<Item> items = GetItemsList(); IEnumerable<string> itemslist = items.Select(itm => itm.Name). Concat(items.Select(itms => itms.Ingredients)). Concat(items.Select(itm => itm.Price.ToString())). Distinct(); foreach (var itms in itemslist) { Console.WriteLine("{0}", itms); }
If the first or the second arguments are null, ArgumentNullException
is thrown.
Ordering operators are useful when we want the result of a select
statement in a particular order. The ordering could be ascending or descending. The declaration syntax for ordering operators is given below:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey> ( IEnumerable<TSource> source, Func<TSource, TKey> keySelector ) public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey> ( IEnumerable<TSource> source, Func<TSource, TKey> keySelector ) ThenBy<TSource, TKey> ( IOrderedSequence<TSource> source, Func<TSource, TKey> keySelector ) public static IOrderedSequence<TSource> ThenBy<TSource, TKey> ( IOrderedSequence<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer ) public static IOrderedSequence<TSource> ThenByDescending<TSource, TKey> ( IOrderedSequence<TSource> source, Func<TSource, TKey> keySelector ) public static IOrderedSequence<TSource> ThenByDescending<TSource, TKey> ( IOrderedSequence<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer ) public static IEnumerable<TSource> Reverse<TSource> ( IEnumerable<TSource> source )
All operators can be composed to order a sequence by multiple keys. The initial ordering is done by the first OrderBy
or OrderByDescending
operator. The second sorting is done by the first ThenBy
or ThenByDescending
operator. The second ThenBy
or ThenByDescending
operators forms the third level of sorting and it goes on like this:
Source OrderBy(….) ThenBy(….) ThenBy(….)
The OrderBy
and ThenBy
methods establish an ascending ordering while the OrderByDescending
and ThenByDescending
are used for sorting in descending order. There is an optional comparer for comparing the key values. If no comparer is specified or if the comparer is null, the default comparer is used.
An ArgumentNullException
is thrown if the source
or keySelector
argument is null. All these sorting operators return an enumerable object.
If one of the sorting operators' resultant objects are enumerated, it first enumerates the source and collects the elements. Then evaluates the keySelector
function once for each element, collecting the key values of OrderBy
and then sorts the elements according to the collected key values.
List<Item> items = GetItemsList(); IEnumerable<Item> itms = items.OrderBy(itm => itm.Name). ThenByDescending(itm => itm.Protein). ThenBy(itm => itm.TotalFat); foreach (var item in itms) { Console.WriteLine("(Ascending) {0} (ThenByDescending) {1} (ThenBy) {2}", item.Name, item.Protein, item.TotalFat); }
This example creates a sequence of all items ordered by item Name
first, the Protein
value of item as second in descending order, and the TotalFat
value, as the third in ascending order.
The previous example is equivalent to the following query:
IEnumerable<Item> itmsQry = from itm in items orderby itm.Name, itm.Protein descending, itm.TotalFat select itm;
The Reverse
operator reverses the sequence of elements. When the source object is enumerated, it enumerates the source sequence collecting the elements and then yielding the elements of the source sequence in reverse order.
Consider the same query for selecting the item with some of its elements in the order. Using the same query, the following code shows how we can reverse the name of each item.
foreach (var item in itmsQry) { char[] name = item.Name.ToArray().Reverse().ToArray(); foreach (char cr in name) { Console.Write(cr + ""); } Console.WriteLine(); }
If any of the source argument is null, the execution returns ArgumentNullException
.
Results of set operations are based on the presence or absence of equivalent elements in the same or other collection. For example, the Distinct
operator removes repeated elements from collections, and the Union
operator returns a unique union of elements from different collections. The various set operators are explained in detail in the following sub-sections:
The Distinct
operator is used for retrieving distinct elements from a sequence. Following is the declaration of the Distinct
operator which uses default equality comparer to compare values:
public static IEnumerable<TSource> Distinct<TSource> ( IEnumerable<TSource> source )
The following declaration the Distinct
operator, uses the specified IEqualityComparer<T>
to compare values.
public static IEnumerable<TSource> Distinct<TSource> ( IEnumerable<TSource> source, IEqualityComparer<TSource> comparer )
Following is the sample code for retrieving the categories from a list of items. The actual execution of the operator takes place when the object is enumerated in the looping statement.
List<Item> items = GetItemsList(); IEnumerable<string> icecreamNames = items. Select(itm => itm.Category).Distinct(); Console.WriteLine("Distinct Icecream categories :"); foreach (var itm in icecreamNames) { Console.WriteLine("{0}", itm); }
If the source is null, and does not contain any values in it, an ArgumentNullException
will be thrown.
This operator returns the differences between two different sequences. The sequence is the concatenation of items into a single sequence using comma separation. A sequence can contain duplicate values. It can be nested and collapsed. Here, the Except
operator is used to returns those elements in the first sequence that do not appear in the second sequence. Also, it does not return those elements in the second sequence that also appear in the first. The declaration of the Except
operator using the default comparer is given as follows:
public static IEnumerable<TSource> Except<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second )
Following is the declaration of the Except
operator using the specified comparer to compare values:
public static IEnumerable<TSource> Except<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer )
Let us consider, we have two collections of strings where the string values present in the second array are also present in the first. The following code shows how to use the Except
operator to fetch the values from the first string array whose values are not present in the second array:
string[] stringsOne = { "Icecreams", "Pastries", "Buiscuits", "Chocolates", "Juices", "Fruits" }; string[] stringsTwo = { "Icecreams", "Pastries" }; IEnumerable<string> stringsOnlyInFirst = stringsOne.Except(stringsTwo); Console.WriteLine("Except Operator Example :"); foreach (string str in stringsOnlyInFirst) { Console.WriteLine("{0}", str); }
If the source does not have any value in it or if the source is null, the operator throws an ArgumentNullException
error.
This Intersect
operator returns items that are common two sequences. Given below are the declarations for the Intersect
operator.
The declaration of the Intersect
operator, using default comparer, is given below:
public static IEnumerable<TSource> Intersect<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second )
The declaration that uses the specified comparer for comparing the values is as follows:
public static IEnumerable<TSource> Intersect<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer )
This operator first fetches the elements from the first sequence and then compares it with the elements present in the second sequence. It keeps the element, if it is also present in the second sequence. In this manner, it compares all the elements of the first sequence with that of the second sequence and then fetches the common ones.
The following code shows an example of fetching the common elements from two sequences using the Intersect
operator.
string[] stringsOne = { "Icecreams", "Pastries", "Buiscuits", "Chocolates", "Juices", "Fruits" }; string[] stringsTwo = { "Icecreams", "Pastries"}; IEnumerable<string> stringsOnlyInFirst = stringsOne.Intersect(stringsTwo); Console.WriteLine("Intersect Operator Example :"); foreach (string str in stringsOnlyInFirst) { Console.WriteLine("{0}", str); }
If the source does not have any value, or has a null value, an ArgumentNullException
is thrown.
The Union
operator is for combining the elements of two different sequences. This is useful for collecting distinct values of different sequences. Given below is the declaration for the Union
operator, which uses the default equality comparer.
public static IEnumerable<TSource> Union<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second )
Following is the declaration of the Union
operator which uses the specified equality operator:
public static IEnumerable<TSource> Union<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer )
This operator first enumerates the first sequence and collects the elements. It then enumerates the second sequence and collects the elements that are not collected already. At the end of enumeration, the operator returns all the distinct elements from both the sequences.
Following is an example for collecting the elements from two different sequences:
string[] stringsOne = { "Icecreams", "Pastries", "Buiscuits", "Chocolates", "Juices", "Fruits" }; string[] stringsTwo = { "Icecreams", "Coffee", "Tea" }; IEnumerable<string> stringsOnlyInFirst = stringsOne.Union(stringsTwo); Console.WriteLine("Union Operator Example :"); foreach (string str in stringsOnlyInFirst) { Console.WriteLine("{0}", str); }
The values returned from this example, will have the Icecreams
value returned only once, as it is already there in the first sequence.
If the source does not have any value, or has a null value, an ArgumentNullException
will be thrown.
This operator is useful for grouping similar elements, based on a common attribute. GroupBy
and ToLookUp
are operators used for grouping elements together.
This operator groups all the elements in a sequence with a common key. This operator returns a collection of objects, which are grouped, based on a distinct key in a sequence. The order is maintained based on the order of elements in source. Elements in each group are in the same order they appear in source.
GroupBy<(Of TSource, TKey>)(IEnumerable<(Of TSource>), Func<(Of TSource, TKey>)) |
Groups the elements according to a specified key selector function. |
GroupBy<(Of TSource, TKey>)(IEnumerable<(Of TSource>), Func<(Of TSource, TKey>), IEqualityComparer<(Of TKey>)) |
Groups the elements according to a specified key selector function and compares the keys by using a specified comparer function. |
GroupBy<(Of TSource, TKey, TElement>)(IEnumerable<(Of TSource>), Func<(Of TSource, TKey>), Func<(Of TSource, TElement>)) |
Groups the elements according to a specified key selector function and selects the resulting elements by using a specified function. |
GroupBy<(Of TSource, TKey, TElement>) (IEnumerable<(Of TSource>), Func< (Of TSource, TKey>), Func<(Of TSource, TElement>), IEqualityComparer< (Of TKey>)) |
Groups the elements of a sequence according to a key selector function. The keys are compared by using a comparer function and the resulting elements are selected using a specified function. |
The following code is an example that groups the items based on the category using the GroupBy
operator.
List<Item> items = GetItemsList(); IEnumerable<IGrouping<string, string>> Items = items. GroupBy(itm => itm.Category, itm => itm.Name); foreach (IGrouping<string, string> itm in Items) { Console.WriteLine("{0}", itm.Key); foreach (string nam in itm) { Console.WriteLine(" {0}", nam); } }
The following is an equivalent of the previous:
IEnumerable<IGrouping<string, string>> CatItems = from Itm in items group Itm.Name by Itm.Category; foreach (IGrouping<string, string> itm in CatItems) { Console.WriteLine("{0}", itm.Key); foreach (string nam in itm) { Console.WriteLine(" {0}", nam); } }
This operator puts elements into a one-to-many dictionary, based on a key selector function. The functionality of Lookup
should not be confused with the dictionary functionality. A dictionary performs a one-to-one mapping of key value pairs, where as, a Lookup
is the grouping of keys to a collection of values. Refer to page number 26 for more examples and explanations regarding ToLookup.
These conversion operators are mainly used for converting the data type of the input objects. Some of these operators can be used to force immediate query execution, instead of deferring it until enumeration.
This operator returns an input as an IEnumerable<T>
. It just changes the compile time type of the source. Following is the declaration of the AsEnumerable
operator.
public static IEnumerable<TSource> AsEnumerable<TSource> ( IEnumerable<TSource> source )
In the next example, we define ArrayList
, which is a non-generic type. Using the AsEnumerable
operator we will try to convert it as an enumerable object.
System.Collections.ArrayList arrList = new System.Collections.ArrayList(4); arrList.Add("String value One"); arrList.Add("String value Two"); arrList.Add(new Icecreams {Category="Icecreams", Name="Vanilla Icecream", Ingredients="vanilla extract, guar gum, cream...", Cholesterol="65mg", Protein="4g", TotalCarbohydrates="26g", TotalFat="16g", Price=9.80}); arrList.Add(new Icecreams {Category="Icecreams", Name="Banana Split Icecream", Ingredients="Banana, guar gum, cream...", Cholesterol="58mg", Protein="6g", TotalCarbohydrates="24g", TotalFat="13g", Price=7.5});
Now use the AsEnumerable operator against the list of strings retrieved from the main array list.
List<string> query1 = arrList.OfType<string>(); IEnumerable<string> qry = query1.AsEnumerable(); Console.WriteLine("Elements of type 'string' are:"); foreach (string str in qry) Console.WriteLine(str);
This operator converts the type of the specified element to a different type. The declaration for the Cast
operator is given as follows:
public static IEnumerable<TResult> Cast<TResult> ( IEnumerable source )
Now let us consider an array list containing objects of type Item
, which we have seen in our previous examples. The ArrayList
operator does not implement IEnumerable<T>
. We can use the Cast
operator to change the type of the result.
ArrayList stringsOne = new ArrayList { new Item { Category = "Icecreams", Name = "Chocolate Fudge Icecream", Ingredients = "cream, milk, mono and diglycerides...", Cholesterol = "50mg", Protein = "4g", TotalCarbohydrates = "35g", TotalFat = "20g", Price = 10.5, FatContents = new FatContent { SaturatedFat = "6g", TransFat = "4g", OtherFat = "6g" } } }; IEnumerable<Item> icecreamsList = stringsOne.Cast<Item>(). Where(o => o.Category == "Icecreams");
ArgumentNullException
is raised if the source is null. If the source element cannot be cast to the type specified, InvalidCastException
will be thrown.
The OfType
operator is used to filter elements based on a particular type. This operator can be used along with the Cast
operator to cast the element of a particular type, so that we can avoid InvalidcastException
.
Following is an array list which contains mixed types of elements. It has strings, integer and double. Out of these values, if we want to extract only the elements that are strings, then we can use the OfType
operator as given below.
ArrayList strings = new ArrayList(5); strings.Add("Icecreams"); strings.Add("Chocolates"); strings.Add("Pastries"); strings.Add(5); strings.Add(2.5); IEnumerable<string> onlyStrings = strings.OfType<string>(); Console.WriteLine("The Elements of type string are :"); foreach (string str in onlyStrings) Console.WriteLine(str);
We will get the ArgumentNullException
if the source value is null.
The ToArray
operator is used for converting a collection into an array. The declaration for the ToArray
operator is as follows:
public static TSource[] ToArray<TSource> ( IEnumerable<TSource> source )
This operator enumerates the sequence and returns the elements in the form of an array. If the source is null, an ArgumentNullException
is thrown.
Following is an example that shows how to get the categories of items in an array using the ToArray
operator.
List<Item> items = GetItemsList(); string[] icecreamNames = items.Select(itm => itm.Category).Distinct().ToArray(); foreach (string nam in icecreamNames) Console.WriteLine(name);
The ToDictionary
operator creates a dictionary from a given sequence. Following is the declaration of the ToDictionary
operator.
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey> ( IEnumerable<TSource> source, Func<TSource, TKey> keySelector ) public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey> ( IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer ) public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement> ( IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector ) public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement> ( IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer )
The KeySelector
and ElementsSelector
functions are useful for creating a key-value pair for the dictionary. If the ElementSelector
is not specified, the element itself is considered as a value by default.
Following is an example for creating the dictionary from the list of items:
List<Icecreams> items = GetItems(); Dictionary<string, Icecreams> icecreamNames = items. ToDictionary(itm => itm.Name); Console.WriteLine("ToDictionary Example: "); foreach (KeyValuePair<string, Icecreams> ice in icecreamNames) Console.WriteLine("Key:{0}, Name:{1}", ice.Key, ice.Value.Ingredients);
In the above example, items
is a list that contains a list of Icecreams
objects that have different properties such as name, category, ingredients, price, etc. Here, KeySelector
is a string which is the key for the dictionary, and the object Icecreams
itself is the element. The resultant dictionary has the item name as a key and the Item
itself as a value.
An ArgumentNullException
is thrown if the source, KeySelector, ElementSelector
, or the key
itself produced by the KeySelector
is null. An ArgumentException
is thrown if the KeySelector
returns the same key for two elements. Key values are compared using a specified comparer, and if it is not specified, the default comparer is used for comparing.
The ToList
operator is used for creating the list from the given sequence. It is enumerated through the sequence and returns the elements as a list.
public static List<TSource> ToList<TSource> ( IEnumerable<TSource> source )
Following is an example that returns the list of item names from strings:
string[] strings = { "Icecreams", "Pastries", "Buiscuits", "Chocolates", "Juices", "Fruits" }; List<string> icecreamsPrices = strings.ToList(); Console.WriteLine("List of items :"); foreach (var icecream in icecreamsPrices) Console.WriteLine("{0}", icecream);
An ArgumentNullException
is thrown if the source is null.
The ToLookup
method returns a lookup which is a one-to-many dictionary that maps the key to a collection of values.
Overloaded List of ToLookup |
Description |
---|---|
ToLookup<(Of TSource, TKey>)(IEnumerable<(Of TSource>), Func<(Of TSource, TKey>)) |
Creates a lookup according to a specified key selector function. |
ToLookup<(Of TSource, TKey>)(IEnumerable<(Of TSource>), Func<(Of TSource, TKey>), IEqualityComparer<(Of TKey>)) |
Creates a lookup according to the specified key selector and the comparer functions. |
ToLookup<(Of TSource, TKey, TElement>)(IEnumerable<(Of TSource>), Func<(Of TSource, TKey>), Func<(Of TSource, TElement>)) |
Creates a lookup according to the specified key selector and element selector functions. |
ToLookup<(Of TSource, TKey, TElement>)(IEnumerable<(Of TSource>), Func<(Of TSource, TKey>), Func<(Of TSource, TElement>), IEqualityComparer<(Of TKey>)) |
Creates a lookup according to the specified key selector, element selector and comparer functions. |
This lookup operator is similar to the dictionary but the dictionary returns one-to-one mapping of keys and values. But the lookup returns the one-to-many mapping of key and collections.
Given below is an example that first gets a collection of items of type Icecream
. The items
collection contains information for a list of different ice-creams and pastries. The look up operator is used for grouping Icecreams
and Pastries
as the key, and then looking up the items
collection for each key.
List<Icecreams> items = GetItems(); Lookup<string, string> lookup = items. ToLookup(p => p.Category, p => p.Name); Console.WriteLine("Lookup Operator Example :"); foreach (IGrouping<string, string> group in lookup) { Console.WriteLine(group.Key); foreach (string str in group) Console.WriteLine(" {0}", str); }
The result from the previous code is shown as follows:
If the source or the key selector is null, ArgumentNullException
is thrown.
This is to find the equality between two sequences. They are considered equal if the number of elements and their value are equal. SequenceEqual
is the operator used for finding the equality between the sequences.
This operator is useful for comparing two sequences and finding their equality. This operator compares the elements in two sequences by using the equality comparer.
public static bool SequenceEqual<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second ) public static bool SequenceEqual<TSource> ( IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer )
Following is an example that compares two sequences using the SequenceEqual
operator:
List<Icecreams> items1 = GetItems(); List<Icecreams> items2 = GetItems(); bool equal = items1.SequenceEqual(items2); Console.WriteLine("The lists {0} equal.", equal ? "are" : "are not");
If the first or second element is null, ArgumentNullExcpetion
is thrown.
This operator is used for creating new sequences. The various generation operators are discussed in detail in the following sub-sections.
This operator returns an empty sequence of a given type.
public static IEnumerable<TResult> Empty<TResult> ()
If the object returned by this operator is enumerated, it yields nothing.
For example, the following code returns an empty sequence of type Item
.
IEnumerable<Item> noItems = Enumerable.Empty<Item>(); Console.WriteLine(noItems.ToString());
This operator is used to generate a sequence of integral numbers within a specified range.
public static IEnumerable<int> Range ( int start, int count )
The code given below shows the use of Range
operator to generate ten numbers.
IEnumerable<int> numbers = Enumerable.Range(1, 10); foreach (int number in numbers) Console.WriteLine(number);
ArgumentOutOfRangeException
is thrown when the count is less than zero.
This operator is used for generating a collection that contains repeated values.
public static IEnumerable<TResult> Repeat<TResult> ( TResult element, int count )
The following code example demonstrates how to use the Repeat
operator to generate a sequence of a repeated values. This code generates the same string, five times.
IEnumerable<string> strings = Enumerable. Repeat("Language Integrated Query", 5); foreach (String str in strings) Console.WriteLine(str);
When the count is less than zero, ArgumentOutOfRangeException
is thrown.
This operator is used to find if any or all the elements in a sequence satisfy a specific condition.
The All
operator is useful in determining whether all values in a collection satisfy a particular condition.
public static bool All<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
This operator does not return any collection. It only returns true or false. The following code uses the All
operator, which returns true if all the items category is equal to Icecreams
.
List<Icecreams> items1 = GetItems(); bool all = items1.All(itm => itm.Category.Equals("Icecreams")); Console.WriteLine("All Items are Icecreams ? {0}", all.ToString());
If the source element is null, ArgumentNullException
is thrown.
This is similar to the All
operator, but it checks if any of the elements in a collection is equal to the specified value.
Following is the declaration of the Any
operator to determine if the sequence contains any of the specified elements:
public static bool Any<TSource> ( IEnumerable<TSource> source )
Following is the declaration of the Any
operator to determine whether any of the elements in the sequence satisfy the condition.
public static bool Any<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
The following example shows the use of Any
operator to find if any of the items category is equal to Pastrie
. If it is, it returns a true value.
List<Icecreams> items1 = GetItems(); bool any = items1.Any(itm => itm.Category.Equals("Pastries")); Console.WriteLine("Item contains Pastries Also ? {0}", any.ToString());
ArgumentNullException
is thrown if the source element is null.
This is similar to Any
operator. It determines whether a sequence contains the specified element.
public static bool Contains<TSource> ( IEnumerable<TSource> source, TSource value ) public static bool Contains<TSource> ( IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer )
The following example shows the code to check whether the specified item is present or not.
List<Icecreams> items = GetItems(); Icecreams itm = new Icecreams {Category="Icecreams", Name="Chocolate Fudge Icecream", Ingredients="cream, milk, mono and diglycerides...", Cholesterol="50mg", Protein="4g", TotalCarbohydrates="35g", TotalFat="20g", Price=10.5 }; bool contains = items.Contains(itm);
If the value is null, ArgumentNullException
is thrown.
These operators are used to compute a value from a collection of values. For example, find the sum of all the numbers in a collection, or find the average of a collection of numbers. We will discuss all the aggregate operators in the following sub-sections:
The Average
operator is useful in computing an average value of a sequence of elements. This operator enumerates the source, invokes the selector function for each element, and computes the average of the resulting values.
If no selector function is specified, the average of the elements themselves is computed. An ArgumentNullException
is thrown if any argument is null, and an OverflowException
is thrown if the sum of the elements is too large.
Following is the code for calculating the average price of all ice-creams:
List<Item> items = GetItemsList(); var averageIcecreamsPrice = items. Select(itm => new { itm.Name, averagePrice = itm.Price.Average(p => p.Price) });
The Count
operator is useful for counting the number of elements in a sequence.
public static int Count<TSource> ( IEnumerable<TSource> source ) public static int Count<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
If the source implements ICollection
, the count can be obtained from the implementation itself.
ArgumentNullException
is thrown if the source is null, and an OverflowException
is thrown if the number of elements in the source is larger than the largest possible value of that type. This can be avoided by using LongCount
.
This is very similar to the Count
method, but it can be used when we expect the result to be a large value.
This operator finds the minimum value from a sequence of values. The Min
operator enumerates the source sequence, invokes the selector function for each element and finds the minimum from a collection values.
public static decimal Min ( IEnumerable<T> source ) public static S Min<TSource> ( IEnumerable<TSource> source, Func<TSource, S> selector )
If a selector function is not specified, the minimum of the elements themselves is computed. If the values implement the IComparable<T>
interface, the values are compared using it. Otherwise the values use the non-generic IComparable
interface.
The following example uses the Min
operator to find out the minimum value from a group of numbers.
int[] integers = { 5, 3, 8, 9, 1, 7}; int minNum = integers.Min(); Console.WriteLine("Minimum Number : {0}", minNum.ToString());
An ArgumentNullException
is thrown if any of the arguments are null, and InvalidOperationException
is thrown if the source contains no elements.
This operator finds the maximum value from a sequence of values. The Max
operator enumerates the source sequence, invokes the selector function for each element, and finds the maximum of the values.
public static decimal Max ( IEnumerable<T> source ) public static S Max<TSource> ( Enumerable<TSource> source, Func<TSource, S> selector )
If a selector function is not specified, the maximum of the elements themselves is computed. If the values implement the IComparable<T>
interface, then the values are compared using it. If the values do not implement this interface then the non-generic IComparable
interface is used for comparing the values.
The following example uses the Max
operator to find out the maximum value from the group of numbers:
int[] integers = { 5, 3, 8, 9, 1, 7}; int maxNum = integers.Max(); Console.WriteLine("Maximum Number : {0}", maxNum.ToString());
If any of the arguments is null, then an ArgumentNullException
is thrown, and InvalidOperationException
is thrown if the source contains no elements.
This operator finds the total of the elements in a sequence. This Sum
operator enumerates the source sequence, invokes the selector function for each element, and finds the total sum.
public static decimal Sum ( IEnumerable<T> source ) public static S Sum<TSource> ( IEnumerable<TSource> source, Func<TSource, S> selector )
If a selector function is not specified, the sum of the elements themselves is computed.
The following example uses the Sum
operator to find out the total of the given numbers:
int[] integers = { 5, 3, 8, 9, 1, 7}; int sum = integers.Sum(); Console.WriteLine("Total of all Numbers : {0}", sum.ToString());
If any of the arguments are null, an ArgumentNullException
is thrown, and an OverflowException
is thrown if the sum is larger than the maximum value for that type.
This operator applies a function over a sequence. It calls the function once for each element in the sequence. The first element of the source is the initial aggregate value. Every time the function is called, the operator passes the current element of the sequence to the function, and the current aggregated value as arguments. The first element of the source is the initial aggregated value. Every time the function returns a result, the previously aggregated value is replaced by the new value returned by it.
public static TSource Aggregate<TSource> ( IEnumerable<TSource> source, Func<TSource, TSource, TSource> func ) public static TAccumulate Aggregate<TSource, TAccumulate> ( IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func ) public static TResult Aggregate<TSource, TAccumulate, TResult> ( IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector )
The following code is an example that shows how to change the order of strings.
string[] numbers = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"}; string reversedOrder = numbers.Aggregate((nums, next) => next + " " + nums); Console.WriteLine("Reversed using Aggregate: {0}", reversedOrder);
For every element in the sequence, the function which adds the values to the aggregate value is called. This happens for all the elements in the sequence. When we take the end result of the aggregate function, we will get the reversed order of the elements. The result would look like this:
Reversed using Aggregate: Ten Nine Eight Seven Six Five Four Three Two One
If the source or function is null, ArgumentNullException
is thrown, and an InvalidOperationException
is thrown if the source does not contain any elements.
These operators divide the input sequence into two or more sections, without rearranging the elements. Also, it returns only one of the sections and ignores the remaining elements.
This operator returns only the specified number of elements from a sequence and skips the remaining elements. It starts from the beginning, and continues until the number is reached and then returns the elements.
It enumerates the source sequence and retrieves the elements one-by-one until the number of elements retrieved is equal to the number given by the count argument. If the count argument is less than or equal to zero, the sequence is not enumerated and no elements are returned.
public static IEnumerable<TSource> Take<TSource> ( IEnumerable<TSource> source, int count )
The following code takes only five elements from a set of ten numbers.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var firstFive = numbers.Take(5); foreach (int num in firstFive) Console.WriteLine(num.ToString());
Following is another example for taking the first two ice-cream's objects from the list of objects retrieved using the GetItems
method:
List<Icecreams> icecreams = GetItems(); IEnumerable<Icecreams> firstTwoIcecreams = icecreams.Take(2); foreach (Icecreams ice in firstTwoIcecreams) Console.WriteLine(ice.Name);
If the source is null, ArgumentNullException
is thrown.
This operator is just the opposite of Take
operator. It skips the specified number of elements in the sequence, and returns the remaining elements.
If the source sequence contains a lower number elements than the value specified by the count argument, it returns nothing. If the count argument is less than or equal to zero, it returns all the elements from the sequence.
public static IEnumerable<TSource> Skip<TSource> ( IEnumerable<TSource> source, int count )
The following code skips the first five elements out of all the numbers, and returns the remaining numbers as sequence.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; var lastFive = numbers.Skip(5); foreach (int num in lastFive) Console.WriteLine(num.ToString());
An ArgumentNullException
is thrown if the source is null.
This operator returns elements from a sequence, while testing each element using the predicate function, and yields the element if the result is true. It fetches only those elements for which the function returns true.
public static IEnumerable<TSource> TakeWhile<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate ) public static IEnumerable<TSource> TakeWhile<TSource> ( IEnumerable<TSource> source, Func<TSource, int, bool> predicate )
The following code gets all the values that are less than five in the given sequence.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var firstFive = numbers.TakeWhile(num => num <= 5); foreach (int num in firstFive) Console.WriteLine(num.ToString());
This operator starts enumerating from the first element and continues until the function returns true. So in the above example, if the value is greater than five, it returns false and skips the elements after that.
This operator starts enumerating a sequence, and skips all the elements while a specified condition is true, and then returns the remaining elements. The following code is a declaration where the first argument is the source sequence, and the second one is the predicate function to check against the source elements.
public static IEnumerable<TSource> SkipWhile<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
Following is the declaration, similar to the above declaration but the integer used in the predicate function is the element index, in the sequence.
public static IEnumerable<TSource> SkipWhile<TSource> ( IEnumerable<TSource> source, Func<TSource, int, bool> predicate )
The following code skips the first five elements and then retrieves the remaining elements in the sequences that are greater than five.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var lastFive = numbers.SkipWhile(num => num >5); foreach (int num in lastFive) Console.WriteLine(num.ToString());
ArgumentNullException
is thrown if the source or the predicate function is null.
This operator starts enumerating a sequence and takes all the elements while a specified condition is true, and then stops enumerating the remaining elements in the sequence. Given below is the syntax for TakeWhile
, where the first argument is the source sequence and the second one is the predicate function to check against the source elements.
public static IEnumerable<TSource> TakeWhile<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
Following is a declaration similar to the above declaration, but the integer used in the predicate function is the element index in the sequence.
public static IEnumerable<TSource> TakeWhile<TSource> ( IEnumerable<TSource> source, Func<TSource, int, bool> predicate )
The following code takes all the items from a list, while the item with price more than 10 is reached. Here, you can see the descending order on the price of the items list to get all the items with price less than or equal to 10.
List<Item> items = GetItemsList(); List<Item> icecreamsWithLesserPrice = from itms in items orderby itms.Price descending select itms; List<Item> topicecreamsWithLesserPrice = icecreamsWithLesserPrice. TakeWhile(item => item.Price <= 10); Console.WriteLine("Items with lesser price"); foreach (Item ItemswithLowPrice in icecreamsWithLesserPrice) Console.WriteLine("Icecream Name: " + ItemswithLowPrice.Name + " Price : " + ItemswithLowPrice.Price);
The variable, topicecreamsWithLesserPrice
will contain only those items with price less than or equal to 10. Once the item with price greater than ten is found, all the remaining items will be ignored, including the first one which does not satisfy the condition.
These operators return a specific or a single element from a sequence.
This operator is useful for replacing an empty sequence with a default value. When an object returned by DefaultIfEmpty
is enumerated, it enumerates the source sequence object and retrieves its elements. If the source is empty, a single element with a default value is returned. The default value for reference and nullable types is null. An ArgumentNullException
is thrown if the source is null.
public static IEnumerable<TSource> DefaultIfEmpty<TSource> ( IEnumerable<TSource> source ) public static IEnumerable<TSource> DefaultIfEmpty<TSource> ( IEnumerable<TSource> source, TSource defaultValue )
In the following example, we use the defaultItem
argument. Then we define a list of items, which is an empty collection. After that, we try to enumerate through the object collection using DefaultIfEmpty
. The defaultItem
argument used for the operator, means that the default item defined is considered if the original sequence is empty.
Item defaultItem = new Item {Category="Icecreams", Name="Default Item Test", Ingredients="cream, milk, ...", Cholesterol="50mg", Protein="4g", TotalCarbohydrates="35g", TotalFat="20g", Price=10.5 , FatContents = new FatContent{SaturatedFat="6g", TransFat="4g", OtherFat="6g"} }; List<Item> items = new List<Item>(); foreach (Item itm in items.DefaultIfEmpty(defaultItem)) Console.WriteLine("{0}", itm.Name);
An ArgumentNullException
is thrown if the source argument is null.
This operator returns an element at a specified index in a sequence. It skips all the elements until the index is specified, and then returns the element from the specified index. If the source sequence implements IList<T>
, the implementation is used for retrieving the element at the specified index.
public static TSource ElementAt<TSource> ( IEnumerable<TSource> source, int index )
An ArgumentNullException
is thrown if the source is null, and an ArgumentOutOfRangeException
is thrown if the index value is less than zero or greater than or equal to the number of elements in source.
This operator is a combination of ElementAt
and DefaultIFEmpty
operators. It checks for a specific item in the source. If it is empty, or the index is not found or out of range, it returns the default element.
public static TSource ElementAtOrDefault<TSource> ( IEnumerable<TSource> source, int index )
The following code is gets the element at index fifteen, but we do not have that many names in the array specified, and because of that the operator ElementAtOrDefault
will return a null to the string. We can check if the item is null or empty using the IsNullOrEmpty
method and change the end result of the method.
string[] strings = { "Icecreams", "Pastries", "Buiscuits", "Chocolates", "Juices", "Fruits" }; int index = 15; string name = strings.ElementAtOrDefault(index); Console.WriteLine("The name at index {0} is '{1}'.", index, String.IsNullOrEmpty(name) ? "<name not found at the specified index>" : name);
An ArgumentNullException
is thrown if the source is null.
This First
operator returns the very first element or the first element that satisfies a condition in a sequence.
public static TSource First<TSource> ( IEnumerable<TSource> source ) public static TSource First<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
Following is an example which shows how to retrieve the first element from a list, with and without using a condition.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int firstNumber = numbers.First(); Console.WriteLine("The First number in the list is: {0}", firstNumber.ToString()); int firstNumberwithCondition = numbers.First(num => num == 10); Console.WriteLine("The First number in the list which satifies the condition is: {0}", firstNumberwithCondition.ToString());
An ArgumentNullException
is thrown if the source is null.
This operator is very similar to the First
operator, but it returns the default value if the source does not have any elements in it.
public static TSource FirstOrDefault<TSource> ( IEnumerable<TSource> source ) public static TSource FirstOrDefault<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
In the following code, FirstOrDefault
returns a default value if the source sequence does not have any value. In the first example, the integer array is empty. The FirstOrDefault
operator returns a zero in this case.
int[] numbers = {}; int firstNumber = numbers.FirstOrDefault(); Console.WriteLine("The First number in the list is: {0}", firstNumber);
The following example shows an empty string array. In this case, the operator returns nothing. We can check the value using IsNullOrEmpty
, and then return the default value or a message.
string[] strings = { }; string firstStringwithCondition = strings.FirstOrDefault(str => str == "Chocolate"); Console.WriteLine("{0}", string.IsNullOrEmpty(firstStringwithCondition) ? "Source is Empty" : firstStringwithCondition);
An ArgumentNullException
is thrown if the source is null.
This operator returns the last element, or the last element which satisfies a predicate condition.
public static TSource Last<TSource> ( IEnumerable<TSource> source ) public static TSource Last<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
The following code shows how to get the last element from a sequence, and also the last element in the sequence that satisfies the given condition.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; int lastNumber = numbers.Last(); Console.WriteLine("The last number in the list is: {0}", lastNumber.ToString()); int lastNumberwithCondition = numbers.Last(num => num == 10); Console.WriteLine("The last number in the list which satisfies the condition is: {0}", lastNumberwithCondition.ToString());
An ArgumentNullException
is thrown if the source is null, and the InvalidOperationException
is thrown if none of the elements satisfy the condition, or the source sequence is empty.
This operator is very similar to the Last
operator, but it returns a default if the source does not have any element in it.
public static TSource LastOrDefault<TSource> ( IEnumerable<TSource> source ) public static TSource LastOrDefault<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
In the following code, LastOrDefault
returns the default value if the source sequence does not have any value. In the first example, the integer array is empty. The LastOrDefault
operator returns zero in this case.
int[] numbers = {}; int lastNumber = numbers.LastOrDefault(); Console.WriteLine("The Last number in the list is: {0}", lastNumber);
The next shows the empty string array. In this case, the operator returns nothing. We can check the value using the IsNullOrEmpty
operator and then return the default value or message required.
string[] strings = { }; string lastStringwithCondition = strings.LastOrDefault(str => str == "Chocolate"); Console.WriteLine("{0}", string.IsNullOrEmpty(lastStringwithCondition) ? "Source is Empty" : lastStringwithCondition);
An ArgumentNullException
is thrown if the source is null.
This operator returns a single element from a sequence. If there is more then one element in the sequence, we should use a condition to get a single element from the list.
public static TSource Single<TSource> ( IEnumerable<TSource> source ) public static TSource Single<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
The following code gets a single element, using a condition from the sequence.
int[] numbers = { 12}; int singleNumber = numbers.Single(); Console.WriteLine("The Single number is: {0}", singleNumber.ToString()); int singleNumberwithCondition = numbers.Single(num => num == 12); Console.WriteLine("Single number in the list which satifies the condition is: {0}", singleNumberwithCondition.ToString());
In the above example, if the sequence is like int[] numbers = {12, 13, 14 , 15, 12}
, then the Single
operator throws an exception.
An ArgumentNullException
is thrown if the source is null and an InvalidOperationException
is thrown if no element satisfies the condition, more than one element satisfies the condition, or the source is empty.
This operator is similar to the Single
operator that returns a single element in the sequence, but the difference is the SingleOrDefault
will return the default value if the source does not have any element in it.
public static TSource SingleOrDefault<TSource> ( IEnumerable<TSource> source ) public static TSource SingleOrDefault<TSource> ( IEnumerable<TSource> source, Func<TSource, bool> predicate )
The following example uses the SingleOrDefault
operator to fetch a single element from the sequence. It returns the default value, zero, if the integer array is empty.
int[] numbers = { }; int singleNumber = numbers.SingleOrDefault(); Console.WriteLine("The Single number in the list is: {0}", singleNumber);
The following code has a condition to fetch the element from the sequence. It returns null if the sequence does not have any element in it. So we can use the IsNullOrEmpty
operator to check the null value, and then return a custom message or value.
string[] strings = { }; string singleStringwithCondition = strings.SingleOrDefault(str => str == "Chocolate"); Console.WriteLine("{0}", string.IsNullOrEmpty(singleStringwithCondition) ? "Source is Empty": singleStringwithCondition);
An ArgumentNullException
is thrown if the source is null, and InvalidOperationException
is thrown if more than one element satisfies the condition.
Operator Type |
Operator |
Description |
---|---|---|
Restriction |
Where OfType |
Filters elements. Filters elements based on their type in the collection. |
Projection |
Select SelectMany |
Selects values. Selects values and combines the resulting collections into one collection. |
Join |
Join GroupJoin |
Joins two sequences based on matching keys and extracts a pair of values from different sources of data. Joins two sequences based on matching keys and groups the resulting matches. |
Concatenation |
Concat |
Concatenates two different collections of data into one. |
Ordering |
OrderBy OrderByDescending ThenBy ThenByDescending Reverse |
Sorts the resulting values in ascending order. Sorts resulting values in descending order. Performs a secondary sort in ascending order. Performs secondary sort in descending order. Reverses the order of the elements in a collection. |
Set |
Distinct Except Intersect Union |
Removes duplicate values, and returns unique values from a collection. Returns only those elements from a collection that do not appear in another collection. Returns only those elements that are common to both collections Returns those elements that appear in either of the two collections. This is a combination of both the collections containing unique elements. |
Grouping |
GroupBy |
Returns grouped elements that are having a common key. |
Conversion |
AsEnumerable Cast OfType ToArray ToDictionary ToList ToLookup |
Returns the input typed as Casts the element to a specified type. This is to filter the values depending on the ability of the values to be cast to a specified type. Converts a collection into an array. Puts elements into a dictionary. Converts a collection into a list. Puts elements into a lookup, which is a one-to-many dictionary. |
Equality |
SequenceEqual |
Determines whether two collections are equal by comparing the elements. |
Element |
DefaultIfEmpty ElementAt ElementAtOrDefault First FirstOrDefault Last LastOrDefault Single SingleOrDefault |
If the collection is empty, it will be replaced with a default valued single collection. Returns the element at a specified index in the collection. Returns an element from a specified index or a default value, if the index is out of range. Returns the first element in a collection or the first element that satisfies a condition. Returns the first element or the first element in a collection that satisfies a condition. If the first element is empty, it returns the default value. This is the combination of Returns the last element or the last element that satisfies the condition in a collection. Returns the last element or the last element that satisfies a condition. If the last element is empty, it returns the default value. Returns the only element or the only element in a collection which satisfies the condition. Returns the only element or the only element in a collection that satisfies the condition. If no such elements exists or if the collection does not contain exactly one element, return the default value. |
Generation |
Empty Range Repeat |
Returns an empty collection. Generates a collection that contains a sequence of numbers. Generates a collection that contains one repeated value. |
Quantifiers |
All Any Contains |
Determines whether all values in a collection satisfy the condition. Determines whether any of the values in a collection satisfy the condition. Determines whether the collection contains any element which is specified. |
Aggregation |
Aggregate Average Count LongCount Max Min Sum |
Accumulates all the values in a collection. Calculates the average of all the values in a collection. Counts the number of elements that satisfy a condition in a collection. Counts the number of elements that satisfy a condition in a large collection. Determines the maximum value in a collection. Determines a minimum value in a collection. Calculates the sum of all the values in a collection. |
Partitioning |
Skip SkipWhile Take Takewhile |
Skips elements in a collection up to the specified number Skips elements in a collection while an element in the collection does not satisfy a condition. Takes all the elements up to a specified position in the collection Takes elements in a collection, while an element in the collection does not satisfy the condition. |
Some of the important query operators have equivalent query expressions. A query expression is a more readable form of query. At compile time, these expressions are translated into calls to the corresponding query methods. The following table shows the list of equivalent expressions for some of the query operators.
Query Operator |
Equivalent Expression |
---|---|
Cast |
Use explicitly typed range variable |
GroupBy |
Group by (or) Group by…into This is for grouping of objects |
GroupJoin |
Join…in…on…equals…into |
Join |
Join…in…on…equals |
OrderBy |
Order by |
OrderByDescending |
Order by…descending |
Select |
select |
SelectMany |
Multiple from clauses |
Where |
where |
ThenBy |
Order by |
ThenByDescending |
Order by…, …descending |
In this chapter, we have seen different query operators supported by LINQ. These operators can be used on the objects whose type implements the IEnumerable<T>
interface, or the interface IQueryable<T>
. All operators differ from one another with respect to the time of execution. The operators like Average
and Sum
, which return a single value, will execute immediately, whereas operators like Select, SelectMany, TakeWhile
, will defer the query execution and return an enumerable object. We can also replace standard query operators with our own implementation that provides additional required services.
3.15.220.201