Chapter 7. Standard Query Operators

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.

Restriction Operators

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.

Where

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.

OfType

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);

Projection Operators

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:

Select

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.

SelectMany

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.

Join Operators

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.

Join

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.

GroupJoin

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 Operator

Concatenation is the operation of appending one sequence to another. Concat is the operator used for concatenating.

Concat

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

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.

Set Operators

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:

Distinct

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.

Except

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.

Intersect

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.

Union

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.

Grouping Operators

This operator is useful for grouping similar elements, based on a common attribute. GroupBy and ToLookUp are operators used for grouping elements together.

GroupBy

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);
}
}

ToLookup

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.

Conversion Operators

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.

AsEnumerable

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);

Cast

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.

OfType

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.

ToArray

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);

ToDictionary

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.

ToList

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.

ToLookup

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:

ToLookup

If the source or the key selector is null, ArgumentNullException is thrown.

Equality Operators

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.

SequenceEqual

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.

Generation Operators

This operator is used for creating new sequences. The various generation operators are discussed in detail in the following sub-sections.

Empty

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());

Range

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.

Repeat

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.

Quantifiers

This operator is used to find if any or all the elements in a sequence satisfy a specific condition.

All

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.

Any

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.

Contains

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.

Aggregation Operators

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:

Average

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) });

Count

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.

LongCount

This is very similar to the Count method, but it can be used when we expect the result to be a large value.

Min

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.

Max

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.

Sum

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.

Aggregate

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.

Partitioning Operators

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.

Take

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.

Skip

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.

TakeWhile

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.

SkipWhile

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.

TakeWhile

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.

Element Operators

These operators return a specific or a single element from a sequence.

DefaultIfEmpty

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.

ElementAt

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.

ElementAtOrDefault

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.

First

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.

FirstOrDefault

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.

Last

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.

LastOrDefault

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.

Single

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.

SingleOrDefault

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.

List of Query Operators

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 IEnumerable<T>

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 First and DefaultIfEmpty operators.

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.

Query Operator Equivalent Expressions

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

Summary

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.220.201