Where, OrderByDescending, and Select are only few of the query operators by LINQ. The LINQ query defines a declarative syntax for the most common operators. There are many more standard query operators available.
The following table lists the standard query operators defined by LINQ.
Standard Query Operators | Description |
---|---|
Where | The Where query operator defines a restriction to filter the collection. |
Select SelectMany | Select and SelectMany define a projection to select values of the result based on a selector function. |
OrderBy ThenBy OrderByDescending ThenByDescending Reverse | These operators are used for sorting. OrderBy sorts values in ascending order, OrderByDescending in descending order. ThenBy and ThenByDescending operators are used for a secondary sort if the first sort gives similar results. Reverse reverses the elements in the collection. |
Join GroupJoin | With the Join operator a join of two collections can be done. This is similar to the JOIN you know from SQL. |
GroupBy | The GroupBy operator groups elements with a common key. |
Any All Contains | Any, All, and Contains are quantifier operators. Any determines if any element in the collection satisfies a predicate function; All determines if all elements in the collection satisfy a predicate. Contains checks whether a specific element is in the collection. |
Take Skip TakeWhile SkipWhile | Take, Skip, TakeWhile, and SkipWhile are partitioning operators. With these, you get a partial result. With Take, you have to specify the number of elements to take from the collection; Skip ignores the specified number of elements and takes the rest. TakeWhile takes the elements as long as a condition is true. |
Distinct Union Intersect Except | Set operators return a collection set. Distinct removes duplicates from a collection. With the exception of Distinct, the other set operators require two collections. Union returns unique elements that appear in either of the two collections. Intersect returns elements that appear in both collections. Except returns elements that appear in just one collection. |
First FirstOrDefault Last LastOrDefault ElementAt ElementAtOrDefault Single SingleOrDefault | Element operators just return one element. First returns the first element that satisfies a condition. FirstOrDefault is similar to First, but it returns a default value of the type if the element is not found. Last returns the last element that satisfies a condition. With ElementAt, you specify the position of the element to return. Single returns the only one element that satisfies a condition. If more than one element satisfies the condition, an exception is thrown. |
Count Sum Min Max Average Aggregate | With aggregate operators, you can get the sum of all values, the number of all elements, the element with the lowest or highest value, an average number, and so on. |
ToArray ToEnumerable ToList ToDictionary OfType<T> | Conversion operators convert the collection to an array, IEnumerable, IList, Idictionary, and so on. |
Empty Range Repeat | Generation operators return a new collection. The collection is empty; with the Empty operator, Range returns a sequence of numbers, and Repeat returns a collection with one repeated value. |
Let’s have a look at some examples for a query.
With the Where operator, you can combine multiple expressions, for example, only get the racers from England that won more than six races:
var britishRacers = from r in Formula1.GetRacers() where r.Country == "England" && r.Wins > 6 orderby r.Wins descending select r;
Iterating through the result, you get these English racers that won more than six races:
Nigel Mansell Damon Hill Graham Hill James Hunt
Now, it would be interesting how many racers are from what country. To get this information, you can use group by with the LINQ query. group r by r.Country groups the racers by the country. The group by expression is using the GroupBy method that returns an object that implements the IGrouping<T> interface. IGrouping<K, T> inherits from IEnumerable<T> and offers an additional property Key that is used with the select that follows to return the key as Country property. The Count property returned is using the Count() extension method to get the number of elements within a group.
var countryCounts = from r in Formula1.GetRacers() group r by r.Country into g orderby g.Count() descending select new { Country = g.Key, Count = g.Count() }; foreach (var t in titleCounts) { Console.WriteLine(t.Country + " " + t.Count); }
Tip | The new operator followed by the curly brackets is a new C# 3.0 operator to create an anonymous type. This is discussed later in this chapter. |
In the result, you can see how many champions are from which country:
Italy 2 Argentina 1 England 6 USA 2 Scotland 2 Australia 2 New Zealand 1 Austria 2 Brazil 3 South Africa 1 Finland 2 France 1 Canada 1 Germany 1 Spain 1
The result is not sorted. With orderby you can sort the collection. You can add orderby before doing the grouping, so all countries are sorted before they are grouped. Here, the result is sorted as well grouped by the country:
var countryCounts = from r in Formula1.GetRacers() orderby r.Country group r by r.Country into g select new { Country = g.Key, Count = g.Count() };
With the previous sort, it is not possible to sort the countries by the number of champions from the country. Placing orderby after the group sorts the result. With orderby after the grouping, you can access the group element for ordering. Here, the order is done first by the count and then by the country that is the Key of the IGrouping<K, T> interface. The list is first ordered by the number of racers in each country in descending order, and if the number of racers is the same among multiple countries, the listings are ordered by country in ascending order.
var countryCounts = from r in Formula1.GetRacers() group r by r.Country into g orderby g.Count() descending, g.Key select new { Country = g.Key, Count = g.Count() };
With the LINQ query expression, you can add several values separated by commas to the orderby expression. The extension methods behind this are OrderBy() for the first element and ThenBy() for the elements that follow.
The result is a sorted list:
England 6 Brazil 3 Australia 2 Austria 2 Finland 2 Italy 2 Scotland 2 USA 2 Argentina 1 Canada 1 France 1 Germany 1 New Zealand 1 South Africa 1 Spain 1
If you do not want to return all the elements, but just would like to return the first three from the results, you can add the Take() extension method to the result:
var countryCounts = (from r in Formula1.GetRacers() group r by r.Country into g orderby g.Count() descending, g.Key select new { Country = g.Key, Count = g.Count() }). Take(3);
In the result, now only three countries are shown:
England 6 Brazil 3 Australia 2
To get just one racer, with the highest number of wins, the collection is sorted by the number of wins, and just the first element returned:
var champion = (from r in Formula1.GetRacers() orderby r.Wins descending select new { Firstname = r.Firstname, Lastname = r.Lastname }). First(); Console.WriteLine(champion.Firstname + " " + champion.Lastname);
Michael Schumacher is the racer who has won the greatest number of races, as shown in the result:
Michael Schumacher
To find out which racers won the most races in relation to the number of races driven, a calculation can be added to the orderby expression. The calculation is also added to the selection to return the calculation with the new objects returned. After the collection is sorted, the first seven racers in the list are taken.
var winRelation = (from r in Formula1.GetRacers() orderby ((float)r.Starts / r.Wins) select new { Name=r.Firstname + " " + r.Lastname, WinRelation = (float)r.Starts / r.Wins}). Take(7); foreach (var r in winRelation) { Console.WriteLine("{0}, {1:#.##}", r.Name, r.WinRelation); }
In the result, you can see the racers that won the most races in regard to races driven. Juan Manual Fangio nearly won every second race.
Juan Manuel Fangio, 2.13 Michal Schumacher, 2.75 Jim Clark, 2.88 Alberto Ascari, 3.2 Jackie Stewart, 3.67 Alain Prost, 3.86 Ayrton Senna, 3.93
If you do the same query as before, now with extension methods, the query looks as follows. Instead of using the orderby and select expressions, the extension methods OrderBy() and Select() are used here:
var winRelation = Formula1.GetRacers(). OrderBy<Racer, float>(r => (float)r.Starts / r.Wins). Select(r => new { Name = r.Firstname + " " + r.Lastname, WinRelation = (float)r.Starts / r.Wins }). Take(7); foreach (var r in winRelation) { Console.WriteLine("{0}, {1:#.##}", r.Name, r.WinRelation); }
Now let’s have a look to the parameter of the extension method r => (float)r.Starts / r.Wins. This is a lambda expression.