The Deferred Operators by Purpose

The deferred Standard Query Operators are organized by their purpose in this section.

Restriction

Restriction operators are used for including or excluding elements of an input sequence.

Where

The Where operator is used to filter elements into a sequence.

Prototypes

The Where operator has two prototypes I will cover.

Example. The First Where Prototype
public static IEnumerable<T> Where<T>(
  this IEnumerable<T> source,
  Func<T, bool> predicate);

This prototype of Where takes an input source sequence and a predicate method delegate and returns an object that, when enumerated, enumerates through the input source sequence yielding elements for which the predicate method delegate returns true.

Because this is an extension method, we do not actually pass the input sequence, as long as we call the Where operator using the instance method syntax.

NOTE

Thanks to extension methods, it is not necessary to pass the first argument to the Standard Query Operators whose first argument has the this keyword modifier, as long as we call the operator on an object of the same type as the first argument.

When calling Where, you pass a delegate to a predicate method. Your predicate method must accept a type T as input, where T is the type of elements contained in the input sequence, and return a bool. The Where operator will call your predicate method for each element in the input sequence and pass it the element. If your predicate method returns true, Where will yield that element into Where's output sequence. If your predicate method returns false, it will not.

Example. The Second Where Prototype
public static IEnumerable<T> Where<T>(
  this IEnumerable<T> source,
  Func<T, int, bool> predicate);

The second Where prototype is identical to the first one, except it specifies that your predicate method delegate receives an additional integer input argument. That argument will be the index number for the element from the input sequence.

The index is zero based, so the index passed for the first element will be zero. The last element will be passed the total number of elements in the sequence minus one.

NOTE

Remember, the index that gets passed will be zero based.

Exceptions

ArgumentNullException is thrown if any of the arguments are null.

Examples

Listing 4-1 is an example calling the first prototype.

Example. An Example of the First Where Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> sequence = presidents.Where(p => p.StartsWith("J"));

foreach (string s in sequence)
  Console.WriteLine("{0}", s);

In the preceding example, restricting a sequence using the first prototype of the Where operator is as simple as calling the Where method on the sequence and passing a lambda expression that returns a bool indicating whether an element should be included in the output sequence. In this example, I am only returning the elements that start with the string "J". This code will produce the following results when Ctrl+F5 is pressed:

Jackson
Jefferson
Johnson

Notice I am passing my predicate method using a lambda expression.

Listing 4-2 shows code calling the second prototype of the Where operator. Notice that this version doesn't even use the actual element itself, p, it only uses the index, i. This code will cause every other element, the ones with an odd index number, to be yielded into the output sequence.

Example. An Example of the Second Where Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
   "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> sequence = presidents.Where((p, i) => (i & 1) == 1);

foreach (string s in sequence)
  Console.WriteLine("{0}", s);

Pressing Ctrl+F5 produces the following results:

Arthur
Bush
Cleveland
Coolidge
Fillmore
Garfield
Harding
Hayes
Jackson
Johnson
Lincoln
McKinley
Nixon
PolkRoosevelt
Taylor
Tyler
Washington

Projection

Projection operators return an output sequence of elements that are generated by selecting elements or instantiating altogether new elements containing portions of elements from an input sequence. The data type of elements in the output sequence may be different than the type of elements in the input sequence.

Select

The Select operator is used to create an output sequence of one type of element from an input sequence of another type of element. It is not necessary that the input element type and the output element type be the same.

Prototypes

There are two prototypes for this operator I will cover.

Example. The First Select Prototype
public static IEnumerable<S> Select<T, S>(
  this IEnumerable<T> source,
  Func<T, S> selector);

This prototype of Select takes an input source sequence and a selector method delegate as input arguments, and it returns an object that, when enumerated, enumerates the input source sequence yielding a sequence of elements of type S. As mentioned before, T and S could be the same type or different types.

When calling Select, you pass a delegate to a selector method via the selector argument. Your selector method must accept a type T as input, where T is the type of elements contained in the input sequence, and it returns a type S element. Select will call your selector method for each element in the input sequence, passing it the element. Your selector method will select the portions of the input element it is interested in, creating a new, possibly different typed element, which may be of an anonymous type, and return it.

Example. The Second Select Prototype
public static IEnumerable<S> Select<T, S>(
  this IEnumerable<T> source,
  Func<T, int, S> selector);

In this prototype of the Select operator, an additional integer is passed to the selector method delegate. This will be the zero-based index of the input element in the input sequence.

Exceptions

ArgumentNullException is thrown if any of the arguments are null.

Examples

An example calling the first prototype is shown in Listing 4-3.

Example. An Example of the First Select Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",

"Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<int> nameLengths = presidents.Select(p => p.Length);

foreach (int item in nameLengths)
  Console.WriteLine(item);

Notice I am passing my selector method using a lambda expression. In this case, my lambda expression will return the length of each element in the input sequence. Also notice that while my input types are strings, my output types are integers.

This code will produce the following results when you press Ctrl+F5:

5
6
8
4
6
9
7
8
10
8
4
8
5
7
8
5
6
7
9
7
7
7
7
8
6
5
6
4
6
9
4
6
6
5
9
10
6

This is a simple example because I am not generating any classes. To provide an even better demonstration of the first prototype, consider the code in Listing 4-4.

Example. Another Example of the First Select Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

var nameObjs = presidents.Select(p => new { p, p.Length });

foreach (var item in nameObjs)
  Console.WriteLine(item);

Notice that my lambda expression is instantiating a new, anonymous type. The compiler will dynamically generate an anonymous type for me that will contain a string p and an int p.Length, and my selector method will return that newly instantiated object. Because the type of the returned element is anonymous, I have no type name to reference it by. So, I cannot assign the output sequence from Select to an IEnumerable of some known type, as I did in the first example where I assigned a variable of type IEnumerable<int> to the output sequence. Therefore, I assign the output sequence to a variable specified with the var keyword.

NOTE

Projection operators whose selector methods instantiate anonymous types to return must have their output sequence assigned to a variable whose type is specified with the var keyword.

When run by pressing Ctrl+F5, this code produces the following output:

{ p = Adams, Length = 5 }
{ p = Arthur, Length = 6 }
{ p = Buchanan, Length = 8 }
{ p = Bush, Length = 4 }
{ p = Carter, Length = 6 }
{ p = Cleveland, Length = 9 }
{ p = Clinton, Length = 7 }
{ p = Coolidge, Length = 8 }
{ p = Eisenhower, Length = 10 }
{ p = Fillmore, Length = 8 }
{ p = Ford, Length = 4 }
{ p = Garfield, Length = 8 }
{ p = Grant, Length = 5 }
{ p = Harding, Length = 7 }
{ p = Harrison, Length = 8 }
{ p = Hayes, Length = 5 }
{ p = Hoover, Length = 6 }
{ p = Jackson, Length = 7 }
{ p = Jefferson, Length = 9 }
{ p = Johnson, Length = 7 }
{ p = Kennedy, Length = 7 }
{ p = Lincoln, Length = 7 }
{ p = Madison, Length = 7 }
{ p = McKinley, Length = 8 }
{ p = Monroe, Length = 6 }

{ p = Nixon, Length = 5 }
{ p = Pierce, Length = 6 }
{ p = Polk, Length = 4 }
{ p = Reagan, Length = 6 }
{ p = Roosevelt, Length = 9 }
{ p = Taft, Length = 4 }
{ p = Taylor, Length = 6 }
{ p = Truman, Length = 6 }
{ p = Tyler, Length = 5 }
{ p = Van Buren, Length = 9 }
{ p = Washington, Length = 10 }
{ p = Wilson, Length = 6 }

There is one problem with this code as it is; I can't control the names of the members of the dynamically generated anonymous class. However, thanks to the new object initialization features of C# 3.0, I could write the lambda expression and specify the anonymous class member names as shown in Listing 4-5.

Example. A Third Example of the First Select Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

var nameObjs = presidents.Select(p => new { LastName = p, Length = p.Length });

foreach (var item in nameObjs)
  Console.WriteLine("{0} is {1} characters long.", item.LastName, item.Length);

Notice that I specified a name for each member in the lambda expression and then accessed each member by name in the Console.WriteLine method call. Here are the results of this code:

Adams is 5 characters long.
Arthur is 6 characters long.
Buchanan is 8 characters long.
Bush is 4 characters long.
Carter is 6 characters long.
Cleveland is 9 characters long.
Clinton is 7 characters long.
Coolidge is 8 characters long.
Eisenhower is 10 characters long.
Fillmore is 8 characters long.
Ford is 4 characters long.
Garfield is 8 characters long.
Grant is 5 characters long.
Harding is 7 characters long.
Harrison is 8 characters long.
Hayes is 5 characters long.
Hoover is 6 characters long.
Jackson is 7 characters long.
Jefferson is 9 characters long.

Johnson is 7 characters long.
Kennedy is 7 characters long.
Lincoln is 7 characters long.
Madison is 7 characters long.
McKinley is 8 characters long.
Monroe is 6 characters long.
Nixon is 5 characters long.
Pierce is 6 characters long.
Polk is 4 characters long.
Reagan is 6 characters long.
Roosevelt is 9 characters long.
Taft is 4 characters long.
Taylor is 6 characters long.
Truman is 6 characters long.
Tyler is 5 characters long.
Van Buren is 9 characters long.
Washington is 10 characters long.
Wilson is 6 characters long.

For the second Select prototype's example, I will embed the index that is passed to my selector method into my output sequence's element type, as shown in Listing 4-6.

Example. An Example of the Second Select Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

var nameObjs = presidents.Select((p, i) => new { Index = i, LastName = p });

foreach (var item in nameObjs)
  Console.WriteLine("{0}.  {1}", item.Index + 1, item.LastName);

This example will output the index number plus one, followed by the name. This code produces the following abbreviated results:

1.  Adams
2.  Arthur
3.  Buchanan
4.  Bush
5.  Carter
...
34.  Tyler
35.  Van Buren
36.  Washington
37.  Wilson

SelectMany

The SelectMany operator is used to create a one-to-many output projection sequence over an input sequence. While the Select operator will return one output element for every input element, SelectMany will return zero or more output elements for every input element.

Prototypes

This operator has two prototypes I will cover.

Example. The First SelectMany Prototype
public static IEnumerable<S> SelectMany<T, S>(
  this IEnumerable<T> source,
  Func<T, IEnumerable<S>> selector);

This prototype of the operator is passed an input source sequence of elements of type T and a selector method delegate, and returns an object that, when enumerated, enumerates the input source sequence, passing each element individually from the input sequence to the selector method. The selector method then returns an object that, when enumerated, yields zero or more elements of type S in an intermediate output sequence. The SelectMany operator will return the concatenated output sequences from each call to your selector method.

Example. The Second SelectMany Prototype
public static IEnumerable<S> SelectMany<T, S>(
  this IEnumerable<T> source,
  Func<T, int, IEnumerable<S>> selector);

This prototype behaves just like the first prototype, except a zero-based index of the element in the input sequence is passed to your selector method.

Exceptions

ArgumentNullException is thrown if any of the arguments are null.

Examples

Listing 4-7 shows an example calling the first prototype.

Example. An Example of the First SelectMany Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<char> chars = presidents.SelectMany(p => p.ToArray());

foreach (char ch in chars)
  Console.WriteLine(ch);

In the preceding example, my selector method receives a string as input, and by calling the ToArray method on that string, it returns an array of chars, which becomes an output sequence of type char.

So, for a single input sequence element, which in this case is a string, my selector method returns a sequence of characters. For each input string, a sequence of characters is output. The SelectMany operator concatenates each of those character sequences into a single character sequence that is returned.

The output of the previous code is

A
d
a
m
s
A
r
t
h
u
r
B
u
c
h
a
n
a
n
B
u
s
h
...
W
a
s
h
i
n
g
t
o
n
W
i
l
s
o
n

That was a pretty simple query but not very demonstrative of a more typical usage. For the next example, I will use the Employee and EmployeeOptionEntry common classes.

I will call the SelectMany operator on the array of Employee elements, and for each Employee element in the array, my selector method delegate will return zero or more elements of the anonymous class I create containing the id and the optionsCount from the array of EmployeeOptionEntry elements for that Employee object. Let's take a look at the code to accomplish this in Listing 4-8.

Example. A More Complex Example of the First SelectMany Prototype
Employee[] employees = Employee.GetEmployeesArray();
EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

var employeeOptions = employees
  .SelectMany(e => empOptions
							                     .Where(eo => eo.id == e.id)
							                     .Select(eo => new {
							                                     id = eo.id,
							                                     optionsCount = eo.optionsCount }));

foreach (var item in employeeOptions)
  Console.WriteLine(item);

In this example, every employee in the Employee array is passed into the lambda expression that is passed into the SelectMany operator. That lambda expression will then retrieve every EmployeeOptionEntry element whose id matches the id of the current employee passed into it by using the Where operator. This is effectively joining the Employee array and the EmployeeOptionEntry array on their id members. The lambda expression's Select operator then creates an anonymous object containing the id and optionsCount members for each matching record in the EmployeeOptionEntry array. This means a sequence of zero or more anonymous objects for each passed employee is returned by the lambda expression. This results in a sequence of sequences that the SelectMany operator then concatenates together.

The previous code produces the following output:

{ id = 1, optionsCount = 2 }
{ id = 2, optionsCount = 10000 }
{ id = 2, optionsCount = 10000 }
{ id = 2, optionsCount = 10000 }
{ id = 3, optionsCount = 5000 }
{ id = 3, optionsCount = 7500 }
{ id = 3, optionsCount = 7500 }
{ id = 4, optionsCount = 1500 }
{ id = 101, optionsCount = 2 }

While a bit contrived, the example in Listing 4-9 shows the second SelectMany prototype being called.

Example. An Example of the Second SelectMany Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<char> chars = presidents
  .SelectMany((p, i) => i < 5 ? p.ToArray() : new char[] { });

foreach (char ch in chars)
  Console.WriteLine(ch);

The lambda expression I provided checks the incoming index and outputs the array of characters from the input string only if the index is less than five. This means I will only get the characters for the first five input strings, as evidenced by the output results:

A
d
a
m
s
A
r
t
h
u
r
B
u
c
h
a
n
a
n
B
u
s
h
C
a
r
t
e
r

Keep in mind that this lambda expression is not all that efficient, particularly if there are a lot of input elements. The lambda expression is getting called for every input element. I am merely returning an empty array after the first five input elements. For better performance, I prefer the Take operator that I cover in the next section for this purpose.

The SelectMany operator is also useful for concatenating multiple sequences together. Read my section on the Concat operator later in this chapter for an example.

Partitioning

The partitioning operators allow you to return an output sequence that is a subset of an input sequence.

Take

The Take operator returns a specified number of elements from the input sequence, starting from the beginning of the sequence.

Prototypes

The Take operator has one prototype I will cover.

Example. The Take Prototype
public static IEnumerable<T> Take<T>(
  this IEnumerable<T> source,
  int count);

This prototype specifies that Take will receive an input source sequence and an integer named count that specifies how many input elements to return, and it will return an object that, when enumerated, will yield the first count number of elements from the input sequence.

If the count value is greater than the number of elements in the input sequence, then each element of the input sequence will be yielded into the output sequence.

Exceptions

ArgumentNullException is thrown if the input source sequence is null.

Examples

Listing 4-10 is an example calling Take.

Example. An Example of the Only Take Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.Take(5);

foreach (string item in items)
  Console.WriteLine(item);

This code will return the first five input elements from the presidents array. The results are

Adams
Arthur
Buchanan
Bush
Carter

In Listing 4-9, I showed some code that I stated would be more efficient if the Take operator were used instead of relying on the index being passed into the lambda expression. Listing 4-11 provides the equivalent code using the Take operator. I will have the exact same results that I had with my code in Listing 4-9, but this code is much more efficient.

Example. Another Example of the Take Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<char> chars = presidents.Take(5).SelectMany(s => s.ToArray());

foreach (char ch in chars)
  Console.WriteLine(ch);

Just like in the SelectMany example using the second prototype, Listing 4-9, the preceding code returns the following results:

A
d
a
m
s
A
r
t
h
u
r
B
u
c
h
a
n
a
n
B
u
s
h
C
a
r
t
e
r

The differences between this code example and Listing 4-9 are that this one takes only the first five elements from the input sequence and only they are passed as the input sequence into SelectMany. The other code example, Listing 4-9, passes all elements into SelectMany; it will just return an empty array for all except the first five.

TakeWhile

The TakeWhile operator yields elements from an input sequence while some condition is true, starting from the beginning of the sequence. The remaining input elements will be skipped.

Prototypes

There are two prototypes for the TakeWhile operator I will cover.

Example. The First TakeWhile Prototype
public static IEnumerable<T> TakeWhile<T>(
  this IEnumerable<T> source,
  Func<T, bool> predicate);

The TakeWhile operator accepts an input source sequence and a predicate method delegate and returns an object that, when enumerated, yields elements until the predicate method returns false. The predicate method receives one element at a time from the input sequence and returns whether the element should be included in the output sequence. If so, it continues processing input elements. Once the predicate method returns false, no other input elements will be processed.

Example. The Second TakeWhile Prototype
public static IEnumerable<T> TakeWhile<T>(
  this IEnumerable<T> source,
  Func<T, int, bool> predicate);

This prototype is just like the first except that the predicate method will also be passed a zero-based index of the element in the input source sequence.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

Listing 4-12 shows an example calling the first TakeWhile prototype.

Example. An Example of Calling the First TakeWhile Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.TakeWhile(s => s.Length < 10);

foreach (string item in items)
  Console.WriteLine(item);

In the preceding code, I wanted to retrieve input elements until I hit one ten or more characters long. Here are the results:

Adams
Arthur
Buchanan
Bush
Carter
Cleveland
Clinton
Coolidge

Eisenhower is the name that caused the TakeWhile operator to stop processing input elements. Now, I will provide an example of the second prototype for the TakeWhile operator in Listing 4-13.

Example. An Example of Calling the Second TakeWhile Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents
  .TakeWhile((s, i) => s.Length < 10 && i < 5);

foreach (string item in items)
  Console.WriteLine(item);

This example will stop when an input element exceeds nine characters in length or when the sixth element is reached, whichever comes first. Here are the results:

Adams
Arthur
Buchanan
Bush
Carter

In this case, it stopped because the sixth element was reached.

Skip

The Skip operator skips a specified number of elements from the input sequence starting from the beginning of the sequence, and yields the rest.

Prototypes

The Skip operator has one prototype I will cover.

Example. The Skip Prototype
public static IEnumerable<T> Skip<T>(
  this IEnumerable<T> source,
  int count);

The Skip operator is passed an input source sequence and an integer named count that specifies how many input elements should be skipped and returns an object that, when enumerated, will skip the first count elements and yield all subsequent elements.

If the value of count is greater than the number of elements in the input sequence, the input sequence will not even be enumerated, and the output sequence will be empty.

Exceptions

ArgumentNullException is thrown if the input source sequence is null.

Examples

Listing 4-14 shows a simple example calling the Skip operator.

Example. An Example of the Only Skip Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.Skip(1);

foreach (string item in items)
  Console.WriteLine(item);

In this example, I wanted to skip the first element. Notice in the following output that I did indeed skip the first input element, "Adams":

Arthur
Buchanan
Bush
...
Van Buren
Washington
Wilson

SkipWhile

The SkipWhile operator will process an input sequence, skipping elements while a condition is true, and then yield the remaining elements into an output sequence.

Prototypes

There are two prototypes for the SkipWhile operator I will cover.

Example. The First SkipWhile Prototype
public static IEnumerable<T> SkipWhile<T>(
  this IEnumerable<T> source,
  Func<T, bool> predicate);

The SkipWhile operator accepts an input source sequence and a predicate method delegate and returns an object that, when enumerated, skips elements while the predicate method returns true. Once the predicate method returns false, the SkipWhile operator yields all subsequent elements. The predicate method receives one element at a time from the input sequence and returns whether the element should be skipped in the output sequence.

SkipWhile has a second prototype that looks like this:

Example. The Second SkipWhile Prototype
public static IEnumerable<T> SkipWhile<T>(
  this IEnumerable<T> source,
  Func<T, int, bool> predicate);

This prototype is just like the first except that our predicate method will also be passed a zero-based index of the element in the input source sequence.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

Listing 4-15 shows an example of the first SkipWhile prototype.

Example. An Example Calling the First SkipWhile Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.SkipWhile(s => s.StartsWith("A"));

foreach (string item in items)
  Console.WriteLine(item);

In this example, I told the SkipWhile method to skip elements as long as they started with the string "A". All the remaining elements will be yielded to the output sequence. Here are the results of the previous query:

Buchanan
Bush
Carter
...
Van Buren
Washington
Wilson

Now, I will try the second SkipWhile prototype, which is shown in Listing 4-16.

Example. An Example of Calling the Second SkipWhile Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents
  .SkipWhile((s, i) => s.Length > 4 && i < 10);

foreach (string item in items)
  Console.WriteLine(item);

In this example, I am going to skip input elements until the length is no longer greater than four characters or until the tenth element is reached. I will then yield the remaining elements. Here are the results:

Bush
Carter
Cleveland
...
Van Buren
Washington
Wilson

In this case, I stopped skipping elements once I hit "Bush", since it was not greater than four characters long, even though its index is only 3.

Concatenation

The concatenation operators allow multiple input sequences of the same type to be concatenated into a single output sequence.

Concat

The Concat operator concatenates two input sequences and yields a single output sequence.

Prototypes

There is one prototype for the Concat operator I will cover.

Example. The Concat Prototype
public static IEnumerable<T> Concat<T>(
  this IEnumerable<T> first,
  IEnumerable<T> second);

In this prototype, two sequences of the same type T of elements are input, as first and second. An object is returned that, when enumerated, enumerates the first input sequence, yielding each element to the output sequence, followed by enumerating the second input sequence, yielding each element to the output sequence.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

Listing 4-17 is an example using the Concat operator, as well as the Take and Skip operators.

Example. An Example Calling the Only Concat Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.Take(5).Concat(presidents.Skip(5));

foreach (string item in items)
  Console.WriteLine(item);

This code takes the first five elements from the input sequence, presidents, and concatenates all but the first five input elements from the presidents sequence. The results should be a sequence with the identical contents of the presidents sequence, and they are:

Adams
Arthur
Buchanan
Bush
Carter
Cleveland
Clinton
Coolidge
Eisenhower
Fillmore
Ford
Garfield
Grant
Harding
Harrison
Hayes
Hoover
Jackson
Jefferson
Johnson
Kennedy
Lincoln
Madison
McKinley
Monroe
Nixon
Pierce
Polk
Reagan

Roosevelt
Taft
Taylor
Truman
Tyler
Van Buren
Washington
Wilson

An alternative technique for concatenating is to call the SelectMany operator on an array of sequences, as shown in Listing 4-18.

Example. An Example Performing Concatention with an Alternative to Using the Concat Operator
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};
IEnumerable<string> items = new[] {
							presidents.Take(5),
							presidents.Skip(5)
							}
							.SelectMany(s => s);

foreach (string item in items)
  Console.WriteLine(item);

In this example, I instantiated an array consisting of two sequences: one created by calling the Take operator on the input sequence and another created by calling the Skip operator on the input sequence. Notice that this is similar to the previous example except that I am calling the SelectMany operator on the array of sequences. Also, while the Concat operator only allows two sequences to be concatenated together, since this technique allows an array of sequences, it may be more useful when needing to concatenate more than two sequences together.

When needing to concatenate more than two sequences together, consider using the SelectMany approach.


Of course, none of this would matter if you did not get the same results as calling the Concat operator. Of course, this isn't a problem, since the results are the same:

Adams
Arthur
Buchanan
Bush
Carter
Cleveland
Clinton
Coolidge
Eisenhower

Fillmore
Ford
Garfield
Grant
Harding
Harrison
Hayes
Hoover
Jackson
Jefferson
Johnson
Kennedy
Lincoln
Madison
McKinley
Monroe
Nixon
Pierce
Polk
Reagan
Roosevelt
Taft
Taylor
Truman
Tyler
Van Buren
Washington
Wilson

Ordering

The ordering operators allow input sequences to be ordered. It is important to notice that both the OrderBy and OrderByDescending operators require an input sequence of type IEnumerable<T> and return a sequence of type IOrderedEnumerable<T>. You cannot pass an IOrderedEnumerable<T> as the input sequence into the OrderBy or OrderByDescending operators. This means that you cannot pass the returned sequence from either the OrderBy or OrderByDescending operators into a subsequent OrderBy or OrderByDescending operator call.

If you need more ordering than is possible with a single call to the OrderBy or OrderByDescending operators, you should subsequently call the ThenBy or ThenByDescending operators. You may chain calls to the ThenBy and ThenByDescending operators to subsequent calls to the ThenBy and ThenByDescending operators, because they accept an IOrderedEnumerable<T> as their input sequence and return an IOrderedEnumerable<T> as their output sequence.

For example, this calling sequence is not allowed:

inputSequence.OrderBy(s => s.LastName).OrderBy(s => s.FirstName)...

Instead, you would use this calling sequence:

inputSequence.OrderBy(s => s.LastName).ThenBy(s => s.FirstName)...

OrderBy

The OrderBy operator allows an input sequence to be ordered based on a keySelector method that will return a key value for each input element, and an ordered output sequence, IOrderedEnumerable<T>, will be yielded in ascending order based on the values of the returned keys.

The sort performed by the OrderBy operator is specified to be unstable. This means it will not preserve the input order of the elements. If two input elements come into the OrderBy operator in a particular order, and the key value for both elements is the same, the order of the output elements could be reversed or maintained, there is no guarantee of either. Even though it appears to be stable, since it is specified as unstable, you must always assume it to be unstable. This means you can never depend on the order of the elements coming out of the call to the OrderBy or OrderByDescending operators for any field except the field specified in the method call. Any order that exists in the sequence passed to either of those operators cannot be assumed to be maintained.

The sorting performed by OrderBy and OrderByDescending is unstable.


Prototypes

The OrderBy operator has two prototypes I will cover.

Example. The First OrderBy Prototype
public static IOrderedEnumerable<T> OrderBy<T, K>(
  this IEnumerable<T> source,
  Func<T, K> keySelector)
where
  K : IComparable<K>;

In this prototype of OrderBy, an input source sequence is passed into the OrderBy operator along with a keySelector method delegate, and an object is returned that, when enumerated, enumerates the source input sequence collecting all the elements, passes each element to the keySelector method thereby retrieving each key, and orders the sequence using the keys.

The keySelector method is passed an input element of type T and will return the field within the element that is to be used as the key value, of type K, for the input element. Types T and K may be the same or different types. The type of the value returned by the keySelector method must implement the IComparable interface.

OrderBy has a second prototype that looks like the following:

Example. The Second OrderBy Prototype
public static IOrderedEnumerable<T> OrderBy<T, K>(
  this IEnumerable<T> source,
  Func<T, K> keySelector,
  IComparer<K> comparer);

This prototype is the same as the first except it allows for a comparer object to be passed. If this version of the OrderBy operator is used, then it is not necessary that type K implement the IComparable interface.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

An example of the first prototype is shown in Listing 4-19.

Example. An Example Calling the First OrderBy Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.OrderBy(s => s.Length);

foreach (string item in items)
  Console.WriteLine(item);

This example orders the presidents by the length of their names. Here are the results:

Bush
Ford
Polk
Taft
Adams
Grant
Hayes
Nixon
Tyler
Arthur
Carter
Hoover
Monroe
Pierce
Reagan
Taylor
Truman
Wilson
Clinton
Harding
Jackson
Johnson
Kennedy
Lincoln
Madison
Buchanan
Coolidge
Fillmore
Garfield
Harrison
McKinley
Cleveland
Jefferson
Roosevelt
Van Buren
Eisenhower
Washington

Now, I will try an example of the second prototype by using my own comparer. Before I explain the code, it might be helpful to examine the IComparer interface.

Example. The IComparer<T> Interface
interface IComparer<T> {
  int Compare(T x, T y);
}

The IComparer interface requires me to implement a single method named Compare. This method will receive two arguments of the same type T and will return an int that is less than zero if the first argument is less than the second, zero if the two arguments are equal, and greater than zero if the second argument is greater than the first. Notice how the generics added to C# 2.0 come to our aid in this interface and prototype.

For this example, to make it clear I am not using any default comparer, I have created a class that implements the IComparer interface, which will order the elements based on their vowel-to-consonant ratios.

Example. My Implementation of the IComparer Interface for an Example Calling the Second OrderBy Prototype
public class MyVowelToConsonantRatioComparer : IComparer<string>
{
  public int Compare(string s1, string s2)
  {
    int vCount1 = 0;
    int cCount1 = 0;
    int vCount2 = 0;
    int cCount2 = 0;

    GetVowelConsonantCount(s1, ref vCount1, ref cCount1);
    GetVowelConsonantCount(s2, ref vCount2, ref cCount2);

    double dRatio1 = (double)vCount1/(double)cCount1;
    double dRatio2 = (double)vCount2/(double)cCount2;

    if(dRatio1 < dRatio2)
      return(-1);
    else if (dRatio1 > dRatio2)
      return(1);
    else
      return(0);
  }

  //  This method is public so my code using this comparer can get the values
  //  if it wants.
  public void GetVowelConsonantCount(string s,
                                     ref int vowelCount,
                                     ref int consonantCount)
  {
    //  DISCLAIMER:  This code is for demonstration purposes only.
    //  This code treats the letter 'y' or 'Y' as a vowel always,
    //  which linguistically speaking, is probably invalid.

string vowels = "AEIOUY";

    //  Initialize the counts.
    vowelCount = 0;
    consonantCount = 0;

    //  Convert to upper case so we are case insensitive.
    string sUpper = s.ToUpper();

    foreach(char ch in sUpper)
    {
      if(vowels.IndexOf(ch) < 0)
        consonantCount++;
      else
        vowelCount++;
    }

    return;
  }
}

That class contains two methods, Compare and GetVowelConsonantCount. The Compare method is required by the IComparer interface. The GetVowelConsonantCount method exists because I needed it internally in the Compare method so that the number of vowels and consonants for a given input string could be obtained. I also wanted the ability to call that same logic from outside the Compare method so that I could obtain the values for display when I looped through my ordered sequence.

The logic of what my comparer is doing isn't that significant. It is highly unlikely that you will ever need to determine the vowel to consonant ratio for a string, much less compare two strings based on that ratio. What is important is how I created a class implementing the IComparer interface by implementing a Compare method. You can see the nitty-gritty implementation of the Compare method by examining the if/else if/else block at the bottom of the Compare method. As you can see, in that block of code, I return −1, 1, or 0, thereby adhering to the contract of the IComparer interface.

Now, I will call the code, which is shown in Listing 4-20.

Example. An Example Calling the Second OrderBy Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

MyVowelToConsonantRatioComparer myComp = new MyVowelToConsonantRatioComparer();

IEnumerable<string> namesByVToCRatio = presidents
  .OrderBy((s => s), myComp);

foreach (string item in namesByVToCRatio)
{
  int vCount = 0;
  int cCount = 0;

  myComp.GetVowelConsonantCount(item, ref vCount, ref cCount);
  double dRatio = (double)vCount / (double)cCount;

  Console.WriteLine(item + " - " + dRatio + " - " + vCount + ":" + cCount);
}

In the preceding example, you can see that I instantiate my comparer before calling the OrderBy operator. I could instantiate it in the OrderBy method call, but then I would not have a reference to it when I want to call it in the foreach loop. Here are the results of this code:

Grant - 0.25 - 1:4
Bush - 0.333333333333333 - 1:3
Ford - 0.333333333333333 - 1:3
Polk - 0.333333333333333 - 1:3
Taft - 0.333333333333333 - 1:3
Clinton - 0.4 - 2:5
Harding - 0.4 - 2:5
Jackson - 0.4 - 2:5
Johnson - 0.4 - 2:5
Lincoln - 0.4 - 2:5
Washington - 0.428571428571429 - 3:7
Arthur - 0.5 - 2:4
Carter - 0.5 - 2:4
Cleveland - 0.5 - 3:6
Jefferson - 0.5 - 3:6
Truman - 0.5 - 2:4
Van Buren - 0.5 - 3:6
Wilson - 0.5 - 2:4
Buchanan - 0.6 - 3:5
Fillmore - 0.6 - 3:5
Garfield - 0.6 - 3:5
Harrison - 0.6 - 3:5
McKinley - 0.6 - 3:5
Adams - 0.666666666666667 - 2:3
Nixon - 0.666666666666667 - 2:3
Tyler - 0.666666666666667 - 2:3
Kennedy - 0.75 - 3:4
Madison - 0.75 - 3:4
Roosevelt - 0.8 - 4:5
Coolidge - 1 - 4:4
Eisenhower - 1 - 5:5
Hoover - 1 - 3:3
Monroe - 1 - 3:3
Pierce - 1 - 3:3
Reagan - 1 - 3:3
Taylor - 1 - 3:3
Hayes - 1.5 - 3:2

As you can see, the presidents with the lower vowel-to-consonant ratios come first.

OrderByDescending

This operator is prototyped and behaves just like the OrderBy operator, except that it orders in descending order.

Prototypes

This operator has two prototypes I will cover.

Example. The First OrderByDescending Prototype
public static IOrderedEnumerable<T> OrderByDescending<T, K>(
  this IEnumerable<T> source,
  Func<T, K> keySelector)
where
  K : IComparable<K>;

This prototype of the OrderByDescending operator behaves just like its equivalent OrderBy prototype except the order will be descending.

The sorting performed by OrderBy and OrderByDescending is unstable.


OrderByDescending has a second prototype that looks like the following:

Example. The Second OrderByDescending Prototype
public static IOrderedEnumerable<T> OrderByDescending<T, K>(
  this IEnumerable<T> source,
  Func<T, K> keySelector,
  IComparer<K> comparer);

This prototype is the same as the first except it allows for a comparer object to be passed. If this version of the OrderByDescending operator is used, then it is not necessary that type K implement the IComparable interface.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

In the example of the first prototype shown in Listing 4-21, I will order the presidents in descending order by their names.

Example. An Example Calling the First OrderByDescending Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.OrderByDescending(s => s);

foreach (string item in items)
  Console.WriteLine(item);

As you can see, the president names are in descending order:

Wilson
Washington
Van Buren
Tyler
Truman
Taylor
Taft
Roosevelt
Reagan
Polk
Pierce
Nixon
Monroe
McKinley
Madison
Lincoln
Kennedy
Johnson
Jefferson
Jackson
Hoover
Hayes
Harrison
Harding
Grant
Garfield
Ford
Fillmore
Eisenhower
Coolidge
Clinton
Cleveland
Carter
Bush
Buchanan
Arthur
Adams

Now, I will try an example of the second OrderByDescending prototype. I will use the same example that I used for the second prototype of the OrderBy operator, except instead of calling the OrderBy operator, I will call the OrderByDescending operator. I will be using the same comparer, MyVowelToConsonantRatioComparer, that I used in that example. The code is shown in Listing 4-22.

Example. An Example Calling the Second OrderByDescending Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

MyVowelToConsonantRatioComparer myComp = new MyVowelToConsonantRatioComparer();

IEnumerable<string> namesByVToCRatio = presidents
  .OrderByDescending((s => s), myComp);

foreach (string item in namesByVToCRatio)
{
  int vCount = 0;
  int cCount = 0;

  myComp.GetVowelConsonantCount(item, ref vCount, ref cCount);
  double dRatio = (double)vCount / (double)cCount;

  Console.WriteLine(item + " - " + dRatio + " - " + vCount + ":" + cCount);
}

This example works just like the equivalent OrderBy example. Here are the results:

Hayes - 1.5 - 3:2
Coolidge - 1 - 4:4
Eisenhower - 1 - 5:5
Hoover - 1 - 3:3
Monroe - 1 - 3:3
Pierce - 1 - 3:3
Reagan - 1 - 3:3
Taylor - 1 - 3:3
Roosevelt - 0.8 - 4:5
Kennedy - 0.75 - 3:4
Madison - 0.75 - 3:4
Adams - 0.666666666666667 - 2:3
Nixon - 0.666666666666667 - 2:3
Tyler - 0.666666666666667 - 2:3
Buchanan - 0.6 - 3:5
Fillmore - 0.6 - 3:5
Garfield - 0.6 - 3:5
Harrison - 0.6 - 3:5
McKinley - 0.6 - 3:5
Arthur - 0.5 - 2:4
Carter - 0.5 - 2:4
Cleveland - 0.5 - 3:6
Jefferson - 0.5 - 3:6
Truman - 0.5 - 2:4
Van Buren - 0.5 - 3:6
Wilson - 0.5 - 2:4
Washington - 0.428571428571429 - 3:7
Clinton - 0.4 - 2:5
Harding - 0.4 - 2:5
Jackson - 0.4 - 2:5
Johnson - 0.4 - 2:5
Lincoln - 0.4 - 2:5
Bush - 0.333333333333333 - 1:3
Ford - 0.333333333333333 - 1:3
Polk - 0.333333333333333 - 1:3
Taft - 0.333333333333333 - 1:3
Grant - 0.25 - 1:4

These results are the same as the equivalent OrderBy example, except the order is reversed. Now, the presidents are listed by their vowel-to-consonant ratio in descending order.

ThenBy

The ThenBy operator allows an input ordered sequence of type IOrderedEnumerable<T> to be ordered based on a keySelector method that will return a key value, and an ordered output sequence of type IOrderedEnumerable<T> will be yielded.

NOTE

Both the ThenBy and ThenByDescending operators accept a different type of input sequence than most LINQ to Objects deferred query operators. They take an IOrderedEnumerable<T> as the input sequence. This means either the OrderBy or OrderByDescending operators must be called first to create an IOrderedEnumerable, on which you can then call the ThenBy or ThenByDescending operators.

The sort performed by the ThenBy operator is stable. This means it will preserve the input order of the elements for equal keys. So, if two input elements come into the ThenBy operator in a particular order, and the key value for both elements is the same, the order of the output elements is guaranteed to be maintained.

NOTE

Unlike OrderBy and OrderByDescending, ThenBy and ThenByDescending are stable sorts.

Prototypes

The ThenBy operator has two prototypes I will cover.

Example. The First ThenBy Prototype
public static IOrderedEnumerable<T> ThenBy<T, K>(
  this IOrderedEnumerable<T> source,
  Func<T, K> keySelector)
where
  K : IComparable<K>;

In this prototype of the ThenBy operator, an ordered input sequence of type IOrderedEnumerable<T> is passed into the ThenBy operator along with a keySelector method delegate. The keySelector method is passed an input element of type T and will return the field within the element that is to be used as the key value, of type K, for the input element. Types T and K may be the same or different types. The value returned by the keySelector method must implement the IComparable interface. The ThenBy operator will order the input sequence in ascending order based on those returned keys.

There is a second prototype like this:

Example. The Second ThenBy Prototype
public static IOrderedEnumerable<T> ThenBy<T, K>(
  this IOrderedEnumerable<T> source,
  Func<T, K> keySelector,
  IComparer<K> comparer);

This prototype is the same as the first except it allows for a comparer object to be passed. If this version of the ThenBy operator is used, then it is not necessary that type K implement the IComparable interface.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

Listing 4-23 shows an example of the first prototype.

Example. An Example Calling the First ThenBy Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.OrderBy(s => s.Length).ThenBy(s => s);

foreach (string item in items)
  Console.WriteLine(item);

This example first orders by the input element length, which in this case is the length of the president's name. It then orders by the element itself. The result is that the names are presented in length order, smallest to largest (ascending), then alphabetically by name, ascending. Here is the proof:

Bush
Ford
Polk
Taft
Adams
Grant
Hayes
Nixon
Tyler
Arthur
Carter
Hoover
Monroe
Pierce
Reagan
Taylor
Truman
Wilson
Clinton
Harding
Jackson
Johnson
Kennedy
Lincoln
Madison
Buchanan
Coolidge
Fillmore
Garfield

Harrison
McKinley
Cleveland
Jefferson
Roosevelt
Van Buren
Eisenhower
Washington

For an example of the second ThenBy operator prototype, I will again use my MyVowelToConsonantRatioComparer comparer object that I introduced in the example of the second OrderBy prototype. However, to call ThenBy, I first must call either OrderBy or OrderByDescending. For this example, I will call OrderBy and order by the number of characters in the name. This way, the names will be ordered ascending by the number of characters, and then within each grouping of names by length, they will be ordered by their vowel to consonant ratio. The example is shown in Listing 4-24.

Example. An Example of the Second ThenBy Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

MyVowelToConsonantRatioComparer myComp = new MyVowelToConsonantRatioComparer();

IEnumerable<string> namesByVToCRatio = presidents
  .OrderBy(n => n.Length)
  .ThenBy((s => s), myComp);

foreach (string item in namesByVToCRatio)
{
  int vCount = 0;
  int cCount = 0;

  myComp.GetVowelConsonantCount(item, ref vCount, ref cCount);
  double dRatio = (double)vCount / (double)cCount;

  Console.WriteLine(item + " - " + dRatio + " - " + vCount + ":" + cCount);
}

This code gives the following results:

Bush - 0.333333333333333 - 1:3
Ford - 0.333333333333333 - 1:3
Polk - 0.333333333333333 - 1:3
Taft - 0.333333333333333 - 1:3
Grant - 0.25 - 1:4
Adams - 0.666666666666667 - 2:3
Nixon - 0.666666666666667 - 2:3
Tyler - 0.666666666666667 - 2:3

Hayes - 1.5 - 3:2
Arthur - 0.5 - 2:4
Carter - 0.5 - 2:4
Truman - 0.5 - 2:4
Wilson - 0.5 - 2:4
Hoover - 1 - 3:3
Monroe - 1 - 3:3
Pierce - 1 - 3:3
Reagan - 1 - 3:3
Taylor - 1 - 3:3
Clinton - 0.4 - 2:5
Harding - 0.4 - 2:5
Jackson - 0.4 - 2:5
Johnson - 0.4 - 2:5
Lincoln - 0.4 - 2:5
Kennedy - 0.75 - 3:4
Madison - 0.75 - 3:4
Buchanan - 0.6 - 3:5
Fillmore - 0.6 - 3:5
Garfield - 0.6 - 3:5
Harrison - 0.6 - 3:5
McKinley - 0.6 - 3:5
Coolidge - 1 - 4:4
Cleveland - 0.5 - 3:6
Jefferson - 0.5 - 3:6
Van Buren - 0.5 - 3:6
Roosevelt - 0.8 - 4:5
Washington - 0.428571428571429 - 3:7
Eisenhower - 1 - 5:5

As I intended, the names are first ordered by their length, then by their vowel to consonant ratio.

ThenByDescending

This operator is prototyped and behaves just like the ThenBy operator, except that it orders in descending order.

Prototypes

This operator has two prototypes I will cover.

Example. The First ThenByDescending Prototype
public static IOrderedEnumerable<T> ThenByDescending<T, K>(
  this IOrderedEnumerable<T> source,
  Func<T, K> keySelector)
where
  K : IComparable<K>;

This prototype of the operator behaves the same as the first prototype of the ThenBy operator, except it orders in descending order.

ThenByDescending has a second prototype that looks like the following:

Example. The Second ThenByDescending Prototype
public static IOrderedEnumerable<T> ThenByDescending<T, K>(
  this IOrderedEnumerable<T> source,
  Func<T, K> keySelector,
  IComparer<K> comparer);

This prototype is the same as the first except it allows for a comparer object to be passed. If this version of the ThenByDescending operator is used, then it is not necessary that K implement the IComparable interface.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

For my example of the first prototype for the ThenByDescending operator, I will use the same basic example I used in the example of the first prototype of the ThenBy operator, except I will call ThenByDescending instead of ThenBy. Listing 4-25 shows this example.

Example. An Example Calling the First ThenByDescending Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items =
  presidents.OrderBy(s => s.Length).ThenByDescending(s => s);

foreach (string item in items)
  Console.WriteLine(item);

This produces output where the names within each name length are sorted alphabetically in descending order, which is the reverse order that the ThenBy operator provided:

Taft
Polk
Ford
Bush
Tyler
Nixon
Hayes
Grant
Adams
Wilson
Truman
Taylor
Reagan
Pierce
Monroe
Hoover

Carter
Arthur
Madison
Lincoln
Kennedy
Johnson
Jackson
Harding
Clinton
McKinley
Harrison
Garfield
Fillmore
Coolidge
Buchanan
Van Buren
Roosevelt
Jefferson
Cleveland
Washington
Eisenhower

For my example of the second prototype of the ThenByDescending operator, which is shown in Listing 4-26, I will use the same example that I did for the second prototype of the ThenBy operator, except I will call ThenByDescending instead of ThenBy.

Example. An Example of the Second ThenByDescending Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

MyVowelToConsonantRatioComparer myComp = new MyVowelToConsonantRatioComparer();

IEnumerable<string> namesByVToCRatio = presidents
  .OrderBy(n => n.Length)
  .ThenByDescending((s => s), myComp);

foreach (string item in namesByVToCRatio)
{
  int vCount = 0;
  int cCount = 0;

  myComp.GetVowelConsonantCount(item, ref vCount, ref cCount);
  double dRatio = (double)vCount / (double)cCount;

  Console.WriteLine(item + " - " + dRatio + " - " + vCount + ":" + cCount);
}

This code provides the following results:

Bush - 0.333333333333333 - 1:3
Ford - 0.333333333333333 - 1:3
Polk - 0.333333333333333 - 1:3
Taft - 0.333333333333333 - 1:3
Hayes - 1.5 - 3:2
Adams - 0.666666666666667 - 2:3
Nixon - 0.666666666666667 - 2:3
Tyler - 0.666666666666667 - 2:3
Grant - 0.25 - 1:4
Hoover - 1 - 3:3
Monroe - 1 - 3:3
Pierce - 1 - 3:3
Reagan - 1 - 3:3
Taylor - 1 - 3:3
Arthur - 0.5 - 2:4
Carter - 0.5 - 2:4
Truman - 0.5 - 2:4
Wilson - 0.5 - 2:4
Kennedy - 0.75 - 3:4
Madison - 0.75 - 3:4
Clinton - 0.4 - 2:5
Harding - 0.4 - 2:5
Jackson - 0.4 - 2:5
Johnson - 0.4 - 2:5
Lincoln - 0.4 - 2:5
Coolidge - 1 - 4:4
Buchanan - 0.6 - 3:5
Fillmore - 0.6 - 3:5
Garfield - 0.6 - 3:5
Harrison - 0.6 - 3:5
McKinley - 0.6 - 3:5
Roosevelt - 0.8 - 4:5
Cleveland - 0.5 - 3:6
Jefferson - 0.5 - 3:6
Van Buren - 0.5 - 3:6
Eisenhower - 1 - 5:5
Washington - 0.428571428571429 - 3:7

Just as I anticipated, the names are ordered first by ascending length, then by the ratio of their vowels to consonants, descending.

Reverse

The reverse operator outputs a sequence of the same type as the input sequence but in the reverse order.

Prototypes

There is one prototype for this operator I will cover.

Example. The Reverse Prototype
public static IEnumerable<T> Reverse<T>(
  this IEnumerable<T> source);

This operator returns an object that, when enumerated, enumerates the elements of the input sequence named source and yields elements for the output sequence in reverse order.

Exceptions

ArgumentNullException is thrown if the source argument is null.

Examples

Listing 4-27 is an example of the prototype of the Reverse operator.

Example. An Example Calling the Reverse Operator
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> items = presidents.Reverse();

foreach (string item in items)
  Console.WriteLine(item);

If this works properly, I should see the presidents in the reverse order of the order in the presidents array. Here are the results of the previous code:

Wilson
Washington
Van Buren
...
Bush
Buchanan
Arthur
Adams

Join

The join operators perform joins across multiple sequences.

Join

The Join operator performs an inner equijoin on two sequences based on keys extracted from each element in the sequences.

Prototypes

The Join operator has one prototype I will cover.

Example. The Join Prototype
public static IEnumerable<V> Join<T, U, K, V>(
  this IEnumerable<T> outer,
  IEnumerable<U> inner,
  Func<T, K> outerKeySelector,
  Func<U, K> innerKeySelector,
  Func<T, U, V> resultSelector);

Notice that the first argument of the method is named outer. Since this is an extension method, the sequence we call the Join operator on will be referred to as the outer sequence.

The Join operator will return an object that, when enumerated, will first enumerate the inner sequence of type U elements, calling the innerKeySelector method once for each element and storing the element, referenced by its key, in a hash table. Next, the returned object will enumerate the outer sequence of type T elements. As the returned object enumerates each outer sequence element, it will call the outerKeySelector method to obtain its key, and retrieve the matching inner sequence elements from the hash table using that key. For each outer sequence element and matching inner sequence element pair, the returned object will call the resultSelector method passing both the outer element and the matching inner element. The resultSelector method will return an instantiated object of type V, which the returned object will place in the output sequence of type V.

The order of the outer sequence elements will be preserved, as will the order of the inner elements within each outer element.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

For this operator's example, instead of using the presidents array that most examples use, I will use the two common classes defined at the beginning of this chapter, Employee and EmployeeOptionEntry.

Here is an example calling the Join operator using those classes. I have formatted the code in Listing 4-28 a little differently than is typical to make each Join argument more easily readable.

Example. Example Code Calling the Join Operator
Employee[] employees = Employee.GetEmployeesArray();
EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

var employeeOptions = employees
  .Join(
							    empOptions,     //  inner sequence
							    e => e.id,      //  outerKeySelector
							    o => o.id,      //  innerKeySelector
							    (e, o) => new   //  resultSelector
							              {
							                id = e.id,
                name = string.Format("{0} {1}", e.firstName, e.lastName),
                options = o.optionsCount
							              });

foreach (var item in employeeOptions)
  Console.WriteLine(item);

In the preceding code, I first obtain a couple arrays of data to join using the two common classes. Because I am calling the Join operator on the employees array, it becomes the outer sequence, and empOptions becomes the inner sequence. Here are the results of the Join operator:

{ id = 1, name = Joe Rattz, options = 2 }
{ id = 2, name = William Gates, options = 10000 }
{ id = 2, name = William Gates, options = 10000 }
{ id = 2, name = William Gates, options = 10000 }
{ id = 3, name = Anders Hejlsberg, options = 5000 }
{ id = 3, name = Anders Hejlsberg, options = 7500 }
{ id = 3, name = Anders Hejlsberg, options = 7500 }
{ id = 4, name = David Lightman, options = 1500 }
{ id = 101, name = Kevin Flynn, options = 2 }

Notice that resultSelector is creating an anonymous class as the element type for the resulting output sequence. You can detect it is an anonymous class because there is no class name specified in the call to new. Because the type is anonymous, it is a necessity that the resulting output sequence be stored in a variable whose type is specified using the var keyword. You cannot specify it is an IEnumerable<> of some type, because there is no named type of which to declare it as an IEnumerable.

When the last operator called is returning a sequence of an anonymous type, you must use the var keyword to store the sequence.


GroupJoin

The GroupJoin operator performs a grouped join on two sequences based on keys extracted from each element in the sequences.

The GroupJoin operator works very similarly to the Join operator with the exception that the Join operator passes a single outer sequence element with a single matching inner sequence element to the resultSelector method. This means that multiple matching inner sequence elements for a single outer sequence element result in multiple calls to resultSelector for the outer sequence element. With the GroupJoin operator, all matching inner sequence elements for a specific outer sequence element are passed to resultSelector as a sequence of that type of element, resulting in the resultSelector method only being called once for each outer sequence element.

Prototypes

This operator has one prototype I will cover.

Example. The GroupJoin Prototype
public static IEnumerable<V> GroupJoin<T, U, K, V>(
  this IEnumerable<T> outer,
  IEnumerable<U> inner,
  Func<T, K> outerKeySelector,
  Func<U, K> innerKeySelector,
  Func<T, IEnumerable<U>, V> resultSelector);

Notice that the first argument of the method is named outer. Since this is an extension method, the sequence the GroupJoin operator is called on will be referred to as the outer sequence.

The GroupJoin operator will return an object that, when enumerated, will first enumerate the inner sequence of type U elements, calling the innerKeySelector method once for each element, and storing the element, referenced by its key, in a hash table. Next, the returned object will enumerate the outer sequence of type T elements. As the returned object enumerates each outer sequence element, it will call the outerKeySelector method to obtain its key, and retrieve the matching inner sequence elements from the hash table using that key. For each outer sequence element, the returned object will call the resultSelector method passing both the outer element and a sequence of the matching inner elements so that resultSelector can return an instantiated object of type V, which the returned object will place in the output sequence of type V.

The order of the outer sequence elements will be preserved, as will the order of the inner elements within each outer element.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

For the GroupJoin example, I will use the same Employee and EmployeeOptionEntry classes that I used in the Join example. My sample code, which appears in Listing 4-29, will join the employees to the options and calculate a sum of the options for each employee using the GroupJoin operator.

Example. An Example of the GroupJoin Operator
Employee[] employees = Employee.GetEmployeesArray();
EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

var employeeOptions = employees
  .GroupJoin(
							    empOptions,
    e => e.id,
    o => o.id,
    (e, os) => new
							                 {
							                   id = e.id,
                   name = string.Format("{0} {1}", e.firstName, e.lastName),
                   options = os.Sum(o => o.optionsCount)
							                 });

foreach (var item in employeeOptions)
  Console.WriteLine(item);

The preceding code is almost identical to the example for the Join operator. However, if you examine the second input argument of the lambda expression passed as the resultSelector method, you will notice that I called the input argument o in the Join example, but I am calling it os in this example. This is because, in the Join example, a single employee option object, o, is passed in this argument, but in the GroupJoin example, a sequence of employee option objects, os, is being passed. Then, the last member of my instantiated anonymous object is being set to the sum of the sequence of employee option objects' optionsCount members using the Sum operator that I will be covering in the next chapter (since it is not a deferred query operator). For now, you just need to understand that the Sum operator has the ability to calculate the sum of each element or a member of each element in an input sequence.

This code will provide the following results:

{ id = 1, name = Joe Rattz, options = 2 }
{ id = 2, name = William Gates, options = 30000 }
{ id = 3, name = Anders Hejlsberg, options = 20000 }
{ id = 4, name = David Lightman, options = 1500 }
{ id = 101, name = Kevin Flynn, options = 2 }

Notice that, in these results, there is one record for each employee containing the sum of all of that employee's option records. Contrast this with the Join operator's example where there was a separate record for each of the employee's option records.

Grouping

The grouping operators assist with grouping elements of a sequence together by a common key.

GroupBy

The GroupBy operator is used to group elements of an input sequence.

Prototypes

All prototypes of the GroupBy operator return a sequence of IGrouping<K, T> elements. IGrouping<K, T> is an interface defined as follows:

Example. The IGrouping<K, T> Interface
public interface IGrouping<K, T> : IEnumerable<T>
{
  K Key { get; }
}

So, an IGrouping is a sequence of type T with a key of type K.

There are four prototypes I will cover.

Example. The First GroupBy Prototype
public static IEnumerable<IGrouping<K, T>> GroupBy<T, K>(
  this IEnumerable<T> source,
  Func<T, K> keySelector);

This prototype of the GroupBy operator returns an object that when enumerated, enumerates the input source sequence, calls the keySelector method, collects each element with its key, and yields a sequence of IGrouping<K, E> instances, where each IGrouping<K, E> element is a sequence of elements with the same key value. Key values are compared using the default equality comparer, EqualityComparerDefault. Said another way, the return value of the GroupBy method is a sequence of IGrouping objects, each containing a key and a sequence of the elements from the input sequence having that same key.

The order of the IGrouping instances will be in the same order that the keys occurred in the source sequence, and each element in the IGrouping sequence will be in the order that element was found in the source sequence.

Example. The Second GroupBy Prototype
public static IEnumerable<IGrouping<K, T>> GroupBy<T, K>(
  this IEnumerable<T> source,
  Func<T, K> keySelector,
  IEqualityComparer<K> comparer);

This prototype of the GroupBy operator is just like the first except instead of using the default equality comparer, EqualityComparerDefault, you provide one.

Example. The Third GroupBy Prototype
public static IEnumerable<IGrouping<K, E>> GroupBy<T, K, E>(
  this IEnumerable<T> source,
  Func<T, K> keySelector,
  Func<T, E> elementSelector);

This prototype of the GroupBy operator is just like the first except instead of the entire source element being the element in the output IGrouping sequence for its key, you may specify which part of the input element is output with the elementSelector.

Example. The Fourth GroupBy Prototype
public static IEnumerable<IGrouping<K, E>> GroupBy<T, K, E>(
  this IEnumerable<T> source,
  Func<T, K> keySelector,
  Func<T, E> elementSelector,
  IEqualityComparer<K> comparer);

This prototype of the GroupBy operator is a combination of the second and third so that you may specify a comparer with the comparer argument, and you may output elements of a different type than the input element type using the elementSelector argument.

Exceptions

ArgumentNullException is thrown if any argument other than the comparer argument is null.

Examples

For my example of the first GroupBy prototype, I will use the common EmployeeOptionEntry class. In this example, in Listing 4-30, I am going to group my EmployeeOptionEntry records by id and display them.

Example. An Example of the First GroupBy Prototype
EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();
IEnumerable<IGrouping<int, EmployeeOptionEntry>> outerSequence =
  empOptions.GroupBy(o => o.id);

//  First enumerate through the outer sequence of IGroupings.
foreach (IGrouping<int, EmployeeOptionEntry> keyGroupSequence in outerSequence)
{
  Console.WriteLine("Option records for employee: " + keyGroupSequence.Key);

  //  Now enumerate through the grouping's sequence of EmployeeOptionEntry elements.
  foreach (EmployeeOptionEntry element in keyGroupSequence)
    Console.WriteLine("id={0} : optionsCount={1} : dateAwarded={2:d}",
      element.id, element.optionsCount, element.dateAwarded);
}

In the preceding code, notice I am enumerating through an outer sequence named outerSequence, where each element is an object implementing IGrouping containing the key, and a sequence of EmployeeOptionEntry elements having that same key.

Here are the results:

Option records for employee: 1
id=1 : optionsCount=2 : dateAwarded=12/31/1999
Option records for employee: 2
id=2 : optionsCount=10000 : dateAwarded=6/30/1992
id=2 : optionsCount=10000 : dateAwarded=1/1/1994
id=2 : optionsCount=10000 : dateAwarded=4/1/2003
Option records for employee: 3
id=3 : optionsCount=5000 : dateAwarded=9/30/1997
id=3 : optionsCount=7500 : dateAwarded=9/30/1998
id=3 : optionsCount=7500 : dateAwarded=9/30/1998
Option records for employee: 4
id=4 : optionsCount=1500 : dateAwarded=12/31/1997
Option records for employee: 101
id=101 : optionsCount=2 : dateAwarded=12/31/1998

For an example of the second GroupBy prototype, let's assume I know that any employee whose id is less than 100 is considered a founder of the company. Those with an id of 100 or greater are not considered founders. My task is to list all option records grouped by the option record's employee founder status. All founders' option records will be grouped together, and all nonfounders' option records will be grouped together.

Now, I need an equality comparer that can handle this key comparison for me. My equality comparer must implement the IEqualityComparer interface. Before examining my comparer, let's take a look at the interface.

Example. The iIEqualityComparer<T> Interface
interface IEqualityComparer<T> {
  bool Equals(T x, T y);
  int GetHashCode(T x);
}

This interface requires me to implement two methods, Equals and GetHashCode. The Equals method is passed two objects of the same type T and returns true if the two objects are considered to be equal or false otherwise. The GetHashCode method is passed a single object and returns a hash code of type int for that object.

A hash code is a numerical value, typically mathematically calculated based on some portion of the data in an object, known as the key, for the purpose of uniquely identifying the object. That calculated hash code functions as the index into some data structure to store that object and find it at a later time. Since it is typical for multiple keys to produce the same hash code, thereby making the hash code truly less than unique, it is also necessary to be able to determine if two keys are equal. This is the purpose of the Equals method.

Here is my class implementing the IEqualityComparer interface.

Example. A Class Implementing the IEqualityComparer Interface for My Second GroupBy Example
public class MyFounderNumberComparer : IEqualityComparer<int>
{
  public bool Equals(int x, int y)
  {
    return(isFounder(x) == isFounder(y));
  }

  public int GetHashCode(int i)
  {
    int f = 1;
    int nf = 100;
    return (isFounder(i) ? f.GetHashCode() : nf.GetHashCode());
  }

  public bool isFounder(int id)
  {
    return(id < 100);
  }
}

In addition to the methods required by the interface, I have added a method, isFounder, to determine if an employee is a founder based on our definition. This just makes the code a little easier to understand. I have made that method public so that I can call it from outside the interface, which you will see me do in my example.

My equality comparer is going to consider any integer less than 100 as representing a founder, and if two integers signify either both founders or both nonfounders, they are considered equal. For the purposes of producing a hash code, I return a hash code of 1 for a founder and 100 for a nonfounder so that all founders end up in the same group, and all nonfounders end up in another group.

My GroupBy example code is in Listing 4-31.

Example. An Example of the Second GroupBy Prototype
MyFounderNumberComparer comp = new MyFounderNumberComparer();

EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();
IEnumerable<IGrouping<int, EmployeeOptionEntry>> opts = empOptions
  .GroupBy(o => o.id, comp);

//  First enumerate through the sequence of IGroupings.
foreach (IGrouping<int, EmployeeOptionEntry> keyGroup in opts)
{
  Console.WriteLine("Option records for: " +
    (comp.isFounder(keyGroup.Key) ? "founder" : "non-founder"));

  //  Now enumerate through the grouping's sequence of EmployeeOptionEntry elements.
  foreach (EmployeeOptionEntry element in keyGroup)
    Console.WriteLine("id={0} : optionsCount={1} : dateAwarded={2:d}",
      element.id, element.optionsCount, element.dateAwarded);
}

In the example, I instantiate my equality comparer object ahead of time, as opposed to doing it in the call to the GroupBy method, so that I can use it to call the isFounder method in the foreach loop. Here are the results from this code:

Option records for: founder
id=1 : optionsCount=2 : dateAwarded=12/31/1999
id=2 : optionsCount=10000 : dateAwarded=6/30/1992
id=2 : optionsCount=10000 : dateAwarded=1/1/1994
id=3 : optionsCount=5000 : dateAwarded=9/30/1997
id=2 : optionsCount=10000 : dateAwarded=4/1/2003
id=3 : optionsCount=7500 : dateAwarded=9/30/1998
id=3 : optionsCount=7500 : dateAwarded=9/30/1998
id=4 : optionsCount=1500 : dateAwarded=12/31/1997
Option records for: non-founder
id=101 : optionsCount=2 : dateAwarded=12/31/1998

As you can see, all employee options records for an employee whose id is less than 100 are grouped with the founders. Otherwise, they are grouped with the nonfounders.

For an example of the third GroupBy prototype, we'll assume we are only interested in getting the dates that the options were awarded for each employee. This code will be very similar to the example for the first prototype.

So in Listing 4-32, instead of returning a sequence of groupings of EmployeeOptionEntry objects, I will have groupings of dates.

Example. An Example of the Third GroupBy Prototype
EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();
IEnumerable<IGrouping<int, DateTime>> opts = empOptions
  .GroupBy(o => o.id, e => e.dateAwarded);

//  First enumerate through the sequence of IGroupings.
foreach (IGrouping<int, DateTime> keyGroup in opts)
{
  Console.WriteLine("Option records for employee: " + keyGroup.Key);

  //  Now enumerate through the grouping's sequence of DateTime elements.
  foreach (DateTime date in keyGroup)
    Console.WriteLine(date.ToShortDateString());
}

Notice that in the call to the GroupBy operator, elementSelector, the second argument, is just returning the dateAwarded member. Because I am returning a DateTime, my IGrouping is now for a type of DateTime, instead of EmployeeOptionEntry.

Just as you would expect, I now have the award dates of the options grouped by employee:

Option records for employee: 1
12/31/1999
Option records for employee: 2
6/30/1992
1/1/1994
4/1/2003
Option records for employee: 3
9/30/1997

9/30/1998
9/30/1998
Option records for employee: 4
12/31/1997
Option records for employee: 101
12/31/1998

For the fourth and final prototype, I need to use an elementSelector method and a comparer object, so I will use a combination of the examples for prototypes two and three. I want to group the dates of awarded options by whether they were awarded to a founding employee or not, where a founding employee is one whose id is less than 100. That code is in Listing 4-33.

Example. An Example of the Fourth GroupBy Prototype
MyFounderNumberComparer comp = new MyFounderNumberComparer();
EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();
IEnumerable<IGrouping<int, DateTime>> opts = empOptions
  .GroupBy(o => o.id, o => o.dateAwarded, comp);

//  First enumerate through the sequence of IGroupings.
foreach (IGrouping<int, DateTime> keyGroup in opts)
{
  Console.WriteLine("Option records for: " +
    (comp.isFounder(keyGroup.Key) ? "founder" : "non-founder"));

  //  Now enumerate through the grouping's sequence of EmployeeOptionEntry elements.
  foreach (DateTime date in keyGroup)
    Console.WriteLine(date.ToShortDateString());
}

In the output, we should see just dates grouped by founders and nonfounders:

Option records for: founder
12/31/1999
6/30/1992
1/1/1994
9/30/1997
4/1/2003
9/30/1998
9/30/1998
12/31/1997
Option records for: non-founder
12/31/1998

Set

The set operators are used to perform mathematical set-type operations on sequences.

The prototypes of the set operators that are covered in this chapter do not work properly for DataSets. For use with DataSets please use the prototypes that are covered in Chapter 10.


Distinct

The Distinct operator removes duplicate elements from an input sequence.

Prototypes

The Distinct operator has one prototype I will cover.

Example. The Distinct Prototype
public static IEnumerable<T> Distinct<T>(
  this IEnumerable<T> source);

This operator returns an object that, when enumerated, enumerates the elements of the input sequence named source and yields any element that is not equal to a previously yielded element. An element is determined to be equal to another element using their GetHashCode and Equals methods.

Isn't it fortuitous that I just covered how and why the GetHashCode and Equals methods are used?

Exceptions

ArgumentNullException is thrown if the source argument is null.

Examples

For this example, I am going to first display the count of the presidents array, next I will concatenate the presidents array with itself, display the count of the resulting concatenated sequence, then call the Distinct operator on that concatenated sequence, and finally display the count of the distinct sequence which should be the same as the initial presidents array.

To determine the count of the two generated sequences, I will use the Count Standard Query Operator. Since it is a nondeferred operator, I will not cover it in this chapter. I will cover it in the next chapter, though. For now, just be aware that it returns the count of the sequence on which it is called.

The code is in Listing 4-34.

Example. An Example of the Distinct Operator
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

//  Display the count of the presidents array.
Console.WriteLine("presidents count:  " + presidents.Count());

//  Concatenate presidents with itself.  Now each element should
//  be in the sequence twice.
IEnumerable<string> presidentsWithDupes = presidents.Concat(presidents);
//  Display the count of the concatenated sequence.
Console.WriteLine("presidentsWithDupes count:  " + presidentsWithDupes.Count());

//  Eliminate the duplicates and display the count.
IEnumerable<string> presidentsDistinct = presidentsWithDupes.Distinct();
Console.WriteLine("presidentsDistinct count:  " + presidentsDistinct.Count());

If this works as I expect, the count of the elements in the presidentsDistinct sequence should equal the count of the elements in the presidents sequence. Will our results indicate success?

presidents count:  37
presidentsWithDupes count:  74
presidentsDistinct count:  37

Yes, they do!

Union

The Union operator returns a sequence of the set union of two source sequences.

Prototypes

This operator has one prototype I will cover.

Example. The Union Prototype
public static IEnumerable<T> Union<T>(
  this IEnumerable<T> first,
  IEnumerable<T> second);

This operator returns an object that, when enumerated, first enumerates the elements of the input sequence named first, yielding any element that is not equal to a previously yielded element, then enumerates the second input sequence, again yielding any element that is not equal to a previously yielded element. An element is determined to be equal to another element using their GetHashCode and Equals methods.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

To demonstrate the difference between the Union operator and the Concat operator I covered previously, in the example in Listing 4-35, I will create a first and second sequence from my presidents array that results in the fifth element being duplicated in both sequences. I will then display the count of the presidents array and the first and second sequences, as well as the count of a concatenated and union sequence.

Example. An Example of the Union Operator
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> first = presidents.Take(5);
IEnumerable<string> second = presidents.Skip(4);
//  Since I only skipped 4 elements, the fifth element
//  should be in both sequences.

IEnumerable<string> concat = first.Concat<string>(second);
IEnumerable<string> union = first.Union<string>(second);

Console.WriteLine("The count of the presidents array is: " + presidents.Count());
Console.WriteLine("The count of the first sequence is: " + first.Count());
Console.WriteLine("The count of the second sequence is: " + second.Count());
Console.WriteLine("The count of the concat sequence is: " + concat.Count());
Console.WriteLine("The count of the union sequence is: " + union.Count());

If this works properly, the concat sequence should have one more element than the presidents array. The union sequence should contain the same number of elements as the presidents array. The proof, however, is in the pudding:

The count of the presidents array is: 37
The count of the first sequence is: 5
The count of the second sequence is: 33
The count of the concat sequence is: 38
The count of the union sequence is: 37

Success!

Intersect

The Intersect operator returns the set intersection of two source sequences.

Prototypes

The Intersect operator has one prototype I will cover.

Example. The Intersect Prototype
public static IEnumerable<T> Intersect<T>(
  this IEnumerable<T> first,
  IEnumerable<T> second);

This operator returns an object that, when enumerated, first enumerates the elements of the input sequence named first, collecting any element that is not equal to a previously collected element. It then enumerates the second input sequence, marking any element that is in both sequences to be yielded. It next enumerates through the marked elements yielding them in the order in which they were collected. An element is determined to be equal to another element using their GetHashCode and Equals methods.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

For my example of the Intersect operator in Listing 4-36, I will use the Take and Skip operators to generate two sequences and get some overlap, just like I did in the Union operator example, where I intentionally duplicated the fifth element. When I call the Intersect operator on those two generated sequences, only the duplicated fifth element should be in the returned intersect sequence. I will display the counts of the presidents array and all the sequences. Lastly, I will enumerate through the intersect sequence displaying each element, which should only be the fifth element of the presidents array.

Example. An Example of the Intersect Operator
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

IEnumerable<string> first = presidents.Take(5);
IEnumerable<string> second = presidents.Skip(4);
//  Since I only skipped 4 elements, the fifth element
//  should be in both sequences.

IEnumerable<string> intersect = first.Intersect(second);

Console.WriteLine("The count of the presidents array is: " + presidents.Count());
Console.WriteLine("The count of the first sequence is: " + first.Count());
Console.WriteLine("The count of the second sequence is: " + second.Count());
Console.WriteLine("The count of the intersect sequence is: " + intersect.Count());

//  Just for kicks, I will display the intersection sequence,
//  which should be just the fifth element.
foreach (string name in intersect)
  Console.WriteLine(name);

If this works the way it should, I should have an intersect sequence with just one element containing the duplicated fifth element of the presidents array, "Carter":

The count of the presidents array is: 37
The count of the first sequence is: 5
The count of the second sequence is: 33
The count of the intersect sequence is: 1
Carter

LINQ rocks! How many times have you needed to perform set-type operations on two collections? Wasn't it a pain? Thanks to LINQ, those days are gone.

Except

The Except operator returns a sequence that contains all the elements of a first sequence that do not exist in a second sequence.

Prototypes

This operator has one prototype I will cover.

Example. The Except Prototype
public static IEnumerable<T> Except<T>(
  this IEnumerable<T> first,
  IEnumerable<T> second);

This operator returns an object that, when enumerated, enumerates the elements of the input sequence named first, collecting any element that is not equal to a previously collected element. It then enumerates the second input sequence, removing from the collection any element that is in both sequences. It next enumerates through the collected elements yielding them in the order in which they were collected. An element is determined to be equal to another element using their GetHashCode and Equals methods.

Exceptions

ArgumentNullException is thrown if any arguments are null.

Examples

For this example, I will use the presidents array that I use in most of the examples. Imagine a scenario where you have a primary data source, the presidents array, with entries that you need to perform some processing on. As you complete the processing of each entry, you want to add it to a collection of processed entries so that if you need to start processing again, you can use the Except operator to produce an exception sequence consisting of the primary data source elements, minus the entries from the processed entry collection. You can then process this exception sequence again without the concern of reprocessing an entry.

For this example in Listing 4-37, I will pretend that I have already processed the first four entries. To obtain a sequence containing the first four elements of the presidents array, I will just call the Take operator on it.

Example. An Example of the Except Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

//  First generate a processed sequence.
IEnumerable<string> processed = presidents.Take(4);

IEnumerable<string> exceptions = presidents.Except(processed);
foreach (string name in exceptions)
  Console.WriteLine(name);

In this example, my results should contain the names of the presidents array after the fourth element, "Bush":

Carter
Cleveland
Clinton
Coolidge
Eisenhower
Fillmore
Ford
Garfield
Grant
Harding
Harrison
Hayes
Hoover
Jackson
Jefferson
Johnson
Kennedy
Lincoln
Madison
McKinley
Monroe
Nixon
Pierce
Polk
Reagan
Roosevelt
Taft
Taylor
Truman
Tyler
Van Buren
Washington
Wilson

That worked just as I would have expected.

Conversion

The conversion operators provide a simple and convenient way of converting sequences to other collection types.

Cast

The Cast operator is used to cast every element of an input sequence to an output sequence of the specified type.

Prototypes

The Cast operator has one prototype I will cover.

Example. The Cast Prototype
public static IEnumerable<T> Cast<T>(
  this IEnumerable source);

The first thing you should notice about the Cast operator is that its first argument, named source, is of type IEnumerable, not IEnumerable<T>, while most of the deferred Standard Query Operators' first arguments are of type IEnumerable<T>. This is because the Cast operator is designed to be called on classes that implement the IEnumerable interface, as opposed to the IEnumerable<T> interface. In particular, we are talking about all the legacy C# collections prior to C# 2.0 and generics.

You can call the Cast operator on a legacy C# collection as long as it implements IEnumerable, and an IEnumerable<T> output sequence will be created. Since most of the Standard Query Operators only work on IEnumerable<T> type sequences, you must call some method like this one, or perhaps the OfType operator that I will cover next, to get a legacy collection converted to a sequence the Standard Query Operators can be called on. This is important when trying to use the Standard Query Operators on legacy collections.

This operator will return an object that, when enumerated, enumerates the source data collection, yielding each element cast to type T. If the element cannot be cast to type T, an exception will be thrown. Because of this, this operator should only be called when it is known that every element in the sequence can be cast to type T.

When trying to perform LINQ queries on legacy collections, don't forget to call Cast or OfType on the legacy collection to create an IEnumerable<T> sequence that the Standard Query Operators can be called on.


Exceptions

ArgumentNullException is thrown if the source argument is null. InvalidCastException is thrown if an element in the input source collection cannot be cast to type T.

Examples

For this example, I will use my common Employee class's GetEmployeesArrayList method to return a legacy, nongeneric ArrayList.

In Listing 4-38 is some code illustrating how the data type of the elements of an ArrayList get cast to elements in a sequence, IEnumerable<T>.

Example. Code Converting an ArrayList to an IEnumerable<T> That Can Be Used with the Typical Standard Query Operators
ArrayList employees = Employee.GetEmployeesArrayList();
Console.WriteLine("The data type of employees is " + employees.GetType());

var seq = employees.Cast<Employee>();
Console.WriteLine("The data type of seq is " + seq.GetType());

var emps = seq.OrderBy(e => e.lastName);
foreach (Employee emp in emps)
  Console.WriteLine("{0} {1}", emp.firstName, emp.lastName);

First, I call the GetEmployeesArrayList method to return an ArrayList of Employee objects, and then I display the data type of the employees variable. Next, I convert that ArrayList to an IEnumerable<T> sequence by calling the Cast operator, and then I display the data type of the returned sequence. Lastly, I enumerate through that returned sequence to prove that the ordering did indeed work.

Here is the output from the code:

The data type of employees is System.Collections.ArrayList
The data type of seq is
System.Linq.Enumerable+<CastIterator>d__b0`1[LINQChapter4.Employee]
Kevin Flynn
William Gates
Anders Hejlsberg
David Lightman
Joe Rattz

You can see the data type of the employees variable is an ArrayList. It is a little more difficult determining what the data type of seq is. We can definitely see it is different, and it looks like a sequence. We can also see the word CastIterator in its type. Have you noticed that when I discuss the deferred operators that they don't actually return the output sequence but really return an object that, when enumerated, would yield the elements to the output sequence? The seq variable's data type displayed in the previous example is just this kind of object. However, this is an implementation detail and could change.

The Cast operator will attempt to cast each element in the input sequence to the specified type. If any of those elements cannot be cast to the specified type, an InvalidCastException exception will be thrown. If it is at all possible that there may be elements of differing types, use the OfType operator instead.


OfType

The OfType operator is used to build an output sequence containing only the elements that can be successfully cast to a specified type.

Prototypes

This operator has one prototype I will cover.

Example. The OfType Prototype
public static IEnumerable<T> OfType<T>(
  this IEnumerable source);

The first thing you should notice about the OfType operator is that, just like the Cast operator, its first argument, named source, is of type IEnumerable, not IEnumerable<T>. Most of the deferred Standard Query Operators' first arguments are of type IEnumerable<T>. This is because the OfType operator is designed to be called on classes that implement the IEnumerable interface, as opposed to the IEnumerable<T> interface. In particular, we are talking about all the legacy C# collections prior to C# 2.0 and generics.

So, you can call the OfType operator on a legacy C# collection as long as it implements IEnumerable, and an IEnumerable<T> output sequence will be created. Since most of the Standard Query Operators only work on IEnumerable<T> type sequences, you must call some method like this one, or perhaps the Cast operator, to get the legacy collection converted to a sequence the Standard Query Operators can be called on. This is important when trying to use the Standard Query Operators on legacy collections.

The OfType operator will return an object that, when enumerated, will enumerate the source sequence, yielding only those elements whose type matches the type specified, T.

The OfType operator differs from the Cast operator in that the Cast operator will attempt to cast every element of the input sequence to type T and yield it to the output sequence. If the cast fails, an exception is thrown. The OfType operator will only attempt to yield the input element if it can be cast to type T. Technically, the element must return true for element e is T for the element to be yielded to the output sequence.

Exceptions

ArgumentNullException is thrown if the source argument is null.

Examples

For the example in Listing 4-39, I am going to create an ArrayList containing objects of my two common classes, Employee and EmployeeOptionEntry. Once I have the ArrayList populated with objects of both classes, I will first call the Cast operator to show how it fails in this circumstance. I will follow that call with a call to the OfType operator showing its prowess in the same situation.

Example. Sample Code Calling the Cast and OfType Operator
ArrayList al = new ArrayList();
al.Add(new Employee { id = 1, firstName = "Joe", lastName = "Rattz" });
al.Add(new Employee { id = 2, firstName = "William", lastName = "Gates" });
al.Add(new EmployeeOptionEntry { id = 1, optionsCount = 0 });
al.Add(new EmployeeOptionEntry { id = 2, optionsCount = 99999999999 });
al.Add(new Employee { id = 3, firstName = "Anders", lastName = "Hejlsberg" });
al.Add(new EmployeeOptionEntry { id = 3, optionsCount = 848475745 });

var items = al.Cast<Employee>();

Console.WriteLine("Attempting to use the Cast operator ...");
try
{
  foreach (Employee item in items)
    Console.WriteLine("{0} {1} {2}", item.id, item.firstName, item.lastName);
}
catch (Exception ex)
{
  Console.WriteLine("{0}{1}", ex.Message, System.Environment.NewLine);
}

Console.WriteLine("Attempting to use the OfType operator ...");
var items2 = al.OfType<Employee>();
foreach (Employee item in items2)
  Console.WriteLine("{0} {1} {2}", item.id, item.firstName, item.lastName);

Once I have the ArrayList created and populated, I call the Cast operator. The next step is to try to enumerate it. This is a necessary step because the Cast operator is deferred. If I never enumerate the results of that query, it will never be performed, and I would not detect a problem. Notice that I wrapped the foreach loop that enumerates the query results with a try/catch block. This is necessary in this case, because I know an exception will be thrown since there are objects of two completely different types. Next, I call the OfType operator and enumerate and display its results. Notice my pluck as I brazenly choose not to wrap my foreach loop in a try/catch block. Of course, in your real production code, you wouldn't want to ignore the protection a try/catch block offers.

Here are the results of this query:

Attempting to use the Cast operator ...
1 Joe Rattz
2 William Gates
Unable to cast object of type 'LINQChapter4.EmployeeOptionEntry' to type
'LINQChapter4.Employee'.

Attempting to use the OfType operator ...
1 Joe Rattz
2 William Gates
3 Anders Hejlsberg

Notice that I was not able to completely enumerate the query results of the Cast operator without an exception being thrown. But, I was able to enumerate the query results of the OfType operator, and only elements of type Employee were included in the output sequence.

The moral of this story is that if it is feasible that the input sequence contains elements of more than one data type, prefer the OfType operator to the Cast operator.

If you are trying to convert a nongeneric collection, such as the legacy collection classes, to an IEnumerable<T> type that can be used with the Standard Query Operators operating on that type, use the OfType operator instead of the Cast operator if it is possible that the input collection could contain objects of differing types.


AsEnumerable

The AsEnumerable operator simply causes its input sequence of type IEnumerable<T> to be returned as type IEnumerable<T>.

Prototypes

The AsEnumerable operator has one prototype I will cover.

Example. The AsEnumerable Prototype
public static IEnumerable<T> AsEnumerable<T>(
  this IEnumerable<T> source);

The preceding prototype declares that the AsEnumerable operator operates on an IEnumerable<T> named source and returns that same sequence typed as IEnumerable<T>. It serves no other purpose than changing the output sequence type at compile time.

This may seem odd since it must be called on an IEnumerable<T>. You may ask, "Why would you possibly need to convert a sequence of type IEnumerable<T> to a sequence of type IEnumerable<T>?" That would be a good question.

The Standard Query Operators are declared to operate on normal LINQ to Objects sequences, those collections implementing the IEnumerable<T> interface. However, other domain's collections, such as those for accessing a database, could choose to implement their own sequence type and operators. Ordinarily, when calling a query operator on a collection of one of those types, a collection-specific operator would be called. The AsEnumerable operator allows the input sequence to be cast as a normal IEnumerable<T> sequence, allowing a Standard Query Operator method to be called.

For example, when I cover LINQ to SQL in a later part of this book, you will see that LINQ to SQL actually uses its own type of sequence, IQueryable<T>, and implements its own operators. The LINQ to SQL operators will be called on sequences of type IQueryable<T>. When you call the Where method on a sequence of type IQueryable<T>, it is the LINQ to SQL Where method that will get called, not the LINQ to Objects Standard Query Operator Where method. In fact, without the AsEnumerable method, you cannot call a Standard Query Operator on a sequence of type IQueryable<T>. If you try to call one of the Standard Query Operators, you will get an exception unless a LINQ to SQL operator exists with the same name, and the LINQ to SQL operator will be called. With the AsEnumerable operator, you can call it to cast the IQueryable<T> sequence to an IEnumerable<T> sequence, thereby allowing Standard Query Operators to be called. This becomes very handy when you need to control in which API an operator is called.

Exceptions

There are no exceptions.

Examples

To better understand this operator, I need a situation where a domain-specific operator is implemented. For that, I need a LINQ to SQL example. I will start with the first LINQ to SQL example in this book from Chapter 1. For your perusal, here is that example.

Example. Reprinted Here for Convenience Is Listing 1-3
using System;
using System.Linq;
using System.Data.Linq;

using nwind;

Northwind db = new Northwind(@"Data Source=.SQLEXPRESS;Initial Catalog=Northwind");

var custs =
  from c in db.Customers
  where c.City == "Rio de Janeiro"
  select c;

foreach (var cust in custs)
  Console.WriteLine("{0}", cust.CompanyName);

Here are the results of that example:

Hanari Carnes
Que Delícia
Ricardo Adocicados

For that example to work, you must add the System.Data.Linq.dll assembly to your project, add a using directive for the nwind namespace, and add the generated entity classes that I will cover in the LINQ to SQL chapters to your project. Additionally, you may need to tweak the connection string.

Let's assume that I need to reverse the order of the records coming from the database for some reason. I am not concerned because I know there is a Reverse operator that I covered earlier in this chapter. Listing 4-40 shows the previous example modified to call the Reverse operator.

Example. Calling the Reverse Operator
Northwind db = new Northwind(@"Data Source=.SQLEXPRESS;Initial Catalog=Northwind");

var custs =
  (from c in db.Customers
   where c.City == "Rio de Janeiro"
   select c)
  .Reverse();

foreach (var cust in custs)
  Console.WriteLine("{0}", cust.CompanyName);

It seems simple enough. As you can see, my only change is to add the call to the Reverse method. The code compiles just fine. Here are the results of the example:

Unhandled Exception: System.NotSupportedException: The query operator 'Reverse' is
not supported.
...

Boy, that seemed like it should have been so simple, what happened? What happened is that there is no Reverse method for the IQueryable<T> interface, so the exception was thrown. I need to use the AsEnumerable method to convert the sequence of type IQueryable<T> to a sequence of type IEnumerable<T> so that when I call the Reverse method, the IEnumerable<T> Reverse method gets called. The code modified to do this is in Listing 4-41.

Example. Calling the AsEnumerable Operator Before Calling the Reverse Operator
Northwind db = new Northwind(@"Data Source=.SQLEXPRESS;Initial Catalog=Northwind");

var custs =
  (from c in db.Customers
   where c.City == "Rio de Janeiro"
   select c)
  .AsEnumerable()
							.Reverse();

foreach (var cust in custs)
  Console.WriteLine("{0}", cust.CompanyName);

Now, I am calling the AsEnumerable method first, followed by the Reverse operator, so the LINQ to Objects Reverse operator will be called. Here are the results:

Ricardo Adocicados
Que Delícia
Hanari Carnes

Those results are in the reverse order of the initial example, so it worked.

Element

The element operators allow you to retrieve single elements from an input sequence.

DefaultIfEmpty

The DefaultIfEmpty operator returns a sequence containing a default element if the input source sequence is empty.

Prototypes

There are two prototypes for the DefaultIfEmpty operator I will cover.

Example. The First DefaultIfEmpty Prototype
public static IEnumerable<T> DefaultIfEmpty<T>(
  this IEnumerable<T> source);

This prototype of the DefaultIfEmpty operator returns an object that, when enumerated, enumerates the input source sequence, yielding each element unless the source sequence is empty, in which case it returns a sequence yielding a single element of default(T). For reference and nullable types, the default value is null.

Unlike all the other element type operators, notice that DefaultIfEmpty returns a sequence of type IEnumerable<T> instead of a type T. There are additional element type operators, but they are not included in this chapter, because they are not deferred operators.

The second prototype allows the default value to be specified.

Example. The Second DefaultIfEmpty Prototype
public static IEnumerable<T> DefaultIfEmpty<T>(
  this IEnumerable<T> source,
  T defaultValue);

This operator is useful for all the other operators that throw exceptions if the input source sequence is empty. Additionally, this operator is useful in conjunction with the GroupJoin operator for producing left outer joins.

Exceptions

ArgumentNullException is thrown if the source argument is null.

Examples

Listing 4-42 shows the example of the first DefaultIfEmpty prototype with an empty sequence. In this example, I will not use the DefaultIfEmpty operator to see what happens. I will search my presidents array for "Jones", return the first element, and if it's not null, output a message.

Example. The First Example for the First DefaultIfEmpty Prototype, Without Using DefaultIfEmpty
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

string jones = presidents.Where(n => n.Equals("Jones")).First();
if (jones != null)
  Console.WriteLine("Jones was found");
else
  Console.WriteLine("Jones was not found");

Here are the results:

Unhandled Exception: System.InvalidOperationException: Sequence contains no elements
...

In the preceding code, the query didn't find any elements equal to "Jones", so an empty sequence was passed to the First operator. The First operator doesn't like empty sequences, so an exception is thrown.

Now, in Listing 4-43, I will call the same code, except I will insert a call to the DefaultIfEmpty operator between the Where operator and the First operator. This way, instead of an empty sequence, a sequence containing a null element will be passed to First.

Example. The Second Example for the First DefaultIfEmpty Prototype, Using DefaultIfEmpty
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

string jones = presidents.Where(n => n.Equals("Jones")).DefaultIfEmpty().First();
if (jones != null)
  Console.WriteLine("Jones was found.");
else
  Console.WriteLine("Jones was not found.");

The results now are

Jones was not found.

For an example of the second prototype, I am allowed to specify the default value for an empty sequence, as shown in Listing 4-44.

Example. An Example for the Second DefaultIfEmpty Prototype
string[] presidents = {
  "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
  "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
  "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
  "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
  "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
  "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

string name =
  presidents.Where(n => n.Equals("Jones")).DefaultIfEmpty("Missing").First();
Console.WriteLine(name);

The results are

Missing

Next, for one last set of examples, I will perform a left outer join using both the GroupJoin and DefaultIfEmpty operators. I will use my two common classes, Employee and EmployeeOptionEntry. In Listing 4-45 is an example without using the DefaultIfEmpty operator.

Example. An Example Without the DefaultIfEmpty Operator
ArrayList employeesAL = Employee.GetEmployeesArrayList();
//  Add a new employee so one employee will have no EmployeeOptionEntry records.
employeesAL.Add(new Employee {
                  id = 102,
                  firstName = "Michael",
                  lastName = "Bolton" });
Employee[] employees = employeesAL.Cast<Employee>().ToArray();
EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

var employeeOptions = employees
  .GroupJoin(
    empOptions,
    e => e.id,
    o => o.id,
    (e, os) => os
      .Select(o => new
                     {
                       id = e.id,
                       name = string.Format("{0} {1}", e.firstName, e.lastName),
                       options = o != null ? o.optionsCount : 0
                     }))
  .SelectMany(r => r);

foreach (var item in employeeOptions)
  Console.WriteLine(item);

There are three things I want to point out about this example. First, it is very similar to the example I presented for the GroupJoin operator example when I discussed it. Second, since my common EmployeeOptionEntry class already has a matching object for every employee in the common Employee class, I am getting the ArrayList of employees and adding a new employee, Michael Bolton, to it so that I will have one employee with no matching EmployeeOptionEntry objects. Third, I am not making a call to the DefaultIfEmpty operator in that example.

The results of this query are

{ id = 1, name = Joe Rattz, options = 2 }
{ id = 2, name = William Gates, options = 10000 }
{ id = 2, name = William Gates, options = 10000 }
{ id = 2, name = William Gates, options = 10000 }

{ id = 3, name = Anders Hejlsberg, options = 5000 }
{ id = 3, name = Anders Hejlsberg, options = 7500 }
{ id = 3, name = Anders Hejlsberg, options = 7500 }
{ id = 4, name = David Lightman, options = 1500 }
{ id = 101, name = Kevin Flynn, options = 2 }

Please notice that, since there were no matching objects in the EmployeeOptionEntry array for employee Michael Bolton, I got no record for that employee in the output sequence. By using the DefaultIfEmpty operator, I can provide a matching default record, as shown in Listing 4-46.

Example. An Example with the DefaultIfEmpty Operator
ArrayList employeesAL = Employee.GetEmployeesArrayList();
//  Add a new employee so one employee will have no EmployeeOptionEntry records.
employeesAL.Add(new Employee {
                  id = 102,
                  firstName = "Michael",
                  lastName = "Bolton" });
Employee[] employees = employeesAL.Cast<Employee>().ToArray();
EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

var employeeOptions = employees
  .GroupJoin(
    empOptions,
    e => e.id,
    o => o.id,
    (e, os) => os
      .DefaultIfEmpty()
      .Select(o => new
                     {
                       id = e.id,
                       name = string.Format("{0} {1}", e.firstName, e.lastName),
                       options = o != null ? o.optionsCount : 0
                     }))
  .SelectMany(r => r);

foreach (var item in employeeOptions)
  Console.WriteLine(item);

In the preceding example, I am still adding an employee object for Michael Bolton with no matching EmployeeOptionEntry objects. I am now calling the DefaultIfEmpty operator. Here are the results of my resulting left outer join:

{ id = 1, name = Joe Rattz, options = 2 }
{ id = 2, name = William Gates, options = 10000 }
{ id = 2, name = William Gates, options = 10000 }
{ id = 2, name = William Gates, options = 10000 }
{ id = 3, name = Anders Hejlsberg, options = 5000 }
{ id = 3, name = Anders Hejlsberg, options = 7500 }
{ id = 3, name = Anders Hejlsberg, options = 7500 }
{ id = 4, name = David Lightman, options = 1500 }
{ id = 101, name = Kevin Flynn, options = 2 }
{ id = 102, name = Michael Bolton, options = 0 }

As you can see, I now have a record for Michael Bolton even though there are no matching EmployeeOptionEntry objects. From the results, you can see Michael Bolton has received no employee options. Indeed, it is no wonder he was always so irritated with that printer.

Generation

The generation operators assist with generating sequences.

Range

The Range operator generates a sequence of integers.

Prototypes

There is one prototype for the Range operator I will cover.

Example. The Range Prototype
public static IEnumerable<int> Range(
  int start,
  int count);

A sequence of integers will be generated starting with the value passed as start and continuing for the number of count.

Notice that this is not an extension method and one of the few Standard Query Operators that does not extend IEnumerable<T>.

NOTE

Range is not an extension method. It is a static method called on System.Linq.Enumerable.

Exceptions

ArgumentOutOfRangeException is thrown if the count is less than zero, or if start plus count minus one is greater than int.MaxValue.

Examples

Example. An Example Calling the Range Operator
IEnumerable<int> ints = Enumerable.Range(1, 10);
foreach(int i in ints)
  Console.WriteLine(i);

Again, I want to stress that I am not calling the Range operator on a sequence. It is a static method of the System.Linq.Enumerable class. There are no surprises here, as the results prove:

1
2
3
4
5
6
7

8
9
10

Repeat

The Repeat operator generates a sequence by repeating a specified element a specified number of times.

Prototypes

The Repeat operator has one prototype I will cover.

Example. The Repeat Prototype
public static IEnumerable<T> Repeat<T>(
  T element,
  int count);

This prototype returns an object that, when enumerated, will yield count number of T elements.

Notice that this is not an extension method and one of the few Standard Query Operators that does not extend IEnumerable<T>.

NOTE

Repeat is not an extension method. It is a static method called on System.Linq.Enumerable.

Exceptions

ArgumentOutOfRangeException is thrown if the count is less than zero.

Examples

In Listing 4-48 I will generate a sequence containing ten elements where each element is the number 2.

Example. Returning a Sequence of Ten Integers All With the Value Two
IEnumerable<int> ints = Enumerable.Repeat(2, 10);
foreach(int i in ints)
  Console.WriteLine(i);

Here are the results of this example:

2
2
2
2
2
2
2
2
2
2

Empty

The Empty operator generates an empty sequence of a specified type.

Prototypes

The Empty operator has one prototype I will cover.

Example. The Empty Prototype
public static IEnumerable<T> Empty<T>();

This prototype returns an object that, when enumerated, will return a sequence containing zero elements of type T.

Notice that this is not an extension method and one of the few Standard Query Operators that does not extend IEnumerable<T>.

NOTE

Empty is not an extension method. It is a static method called on System.Linq.Enumerable.

Exceptions

There are no exceptions.

Examples

In Listing 4-49 I generate an empty sequence of type string using the Empty operator and display the Count of the generated sequence, which should be zero since the sequence is empty.

Example. An Example to Return an Empty Sequence of Strings
IEnumerable<string> strings = Enumerable.Empty<string>();
foreach(string s in strings)
  Console.WriteLine(s);
Console.WriteLine(strings.Count());

Here is the output of the preceding code:

0

Since the sequence is empty, there are no elements to display in the foreach loop, so I added the display of the count of the number of elements in the sequence.

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

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