9.2 Querying an Array of int Values Using LINQ

Figure 9.2 shows how to use LINQ to Objects to query an array of integers, selecting elements that satisfy a set of conditions—a process called filtering. Iteration statements that filter arrays focus on the process of getting the results—iterating through the elements and checking whether they satisfy the desired criteria. LINQ specifies the conditions that selected elements must satisfy. This is known as declarative programming—as opposed to imperative programming (which we’ve been doing so far) in which you specify the actual actions to perform a task. The query in lines 22–24 specifies that the results should consist of all the ints in the values array that are greater than 4. It does not specify how those results are obtained—the C# compiler generates all the necessary code, which is one of the great strengths of LINQ. Using LINQ to Objects requires the System.Linq namespace (line 4).

Fig. 9.2 LINQ to Objects using an int array.

Alternate View

  1   // Fig. 9.2: LINQWithSimpleTypeArray.cs
  2   // LINQ to Objects using an int array.
  3   using System;
  4   using System.Linq;
  5
  6   class LINQWithSimpleTypeArray
  7   {
  8      static void Main()
  9      {
 10         // create an integer array 
 11         var values = new[] {2, 9, 5, 0, 3, 7, 1, 4, 8, 5};
 12
 13           // display original values 
 14         Console.Write("Original array:");
 15         foreach (var element in values)
 16         {
 17            Console.Write($" {element}");
 18         }
 19
 20         // LINQ query that obtains values greater than 4 from the array 
 21         var filtered =
 22            from value in values // data source is values
 23            where value > 4     
 24            select value;       
 25
 26           // display filtered results 
 27         Console.Write("
Array values greater than 4:");
 28         foreach (var element in filtered)
 29         {
 30            Console.Write($" {element} ");
 31         }
 32
 33         // use orderby clause to sort original values in ascending order 
 34         var sorted =
 35            from value in values // data source is values
 36            orderby value       
 37            select value;       
 38
 39         // display sorted results
 40         Console.Write("
Original array, sorted:");
 41         foreach (var element in sorted)
 42         {
 43         Console.Write($" {element} ");
 44         }
 45
 46         // sort the filtered results into descending order 
 47         var sortFilteredResults =
 48         from value in filtered   // data source is LINQ query filtered 
 49         orderby value descending
 50         select value;           
 51
 52         // display the sorted results 
 53         Console.Write(
 54          "
Values greater than 4, descending order (two queries):");
 55         foreach (var element in sortFilteredResults)
 56         {
 57         Console.Write($" {element} ");
 58         }
 59
 60           // filter original array and sort results in descending order 
 61         var sortAndFilter =
 62         from value in values     // data source is values
 63         where value > 4         
 64         orderby value descending
 65         select value;           
 66
 67           // display the filtered and sorted results 
 68         Console.Write(
 69            "
Values greater than 4, descending order (one query):");
 70         foreach (var element in sortAndFilter)
 71         {
 72            Console.Write($" {element} ");
 73         }
 74
 75         Console.WriteLine();
 76      }
 77   }

Original array: 2 9 5 0 3 7 1 4 8 5
Array values greater than 4: 9 5 7 8 5
Original array, sorted: 0 1 2 3 4 5 5 7 8 9
Values greater than 4, descending order (two queries): 9 8 7 5 5
Values greater than 4, descending order (one query): 9 8 7 5 5

9.2.1 The from Clause

A LINQ query begins with a from clause (line 22), which specifies a range variable (value) and the data source to query (values). The range variable represents each item in the data source (one at a time), much like the control variable in a foreach statement. Since value is assigned one element at a time from the array values—an int array—the compiler infers that value should be of type int. You also may declare the range variable’s type explicitly between the from keyword and the range-variable’s name.

Introducing the range variable in the from clause allows the IDE to provide Intelli-Sense as you type the rest of the query. When you enter the range variable’s name followed by a dot (.) in the code editor, the IDE displays the range variable’s methods and properties, making it easier for you to construct queries.

Implicitly Typed Local Variables

Typically, implicitly typed local variables (declared with var) are used for the collections of data returned by LINQ queries, as we do in lines 21, 34, 47 and 61. We also use this feature to declare the control variable in the foreach statements.

9.2.2 The where Clause

If the condition in the where clause (line 23) evaluates to true, the element is selected— i.e., it’s included in the results. Here, the ints in the array are included in the result only if they’re greater than 4. An expression that takes a value and returns true or false by testing a condition on that value is known as a predicate.

9.2.3 The select Clause

For each item in the data source, the select clause (line 24) determines what value appears in the results. In this case, it’s the int that the range variable currently represents, but you’ll soon see that the select clause may contain an expression that transforms a value before including it in the results. Most LINQ queries end with a select clause.

9.2.4 Iterating Through the Results of the LINQ Query

Lines 28–31 use a foreach statement to display the query results. As you know, a foreach statement can iterate through the contents of an array, allowing you to process each element in the array. Actually, the foreach statement can iterate through the contents of arrays, collections and the results of LINQ queries. The foreach statement in lines 28–31 iterates over the query result filtered, displaying each of its int items.

LINQ vs. Iteration Statements

It would be simple to display the integers greater than 4 using a iteration statement that tests each value before displaying it. However, this would intertwine the code that selects elements and the code that displays them. With LINQ, these are kept separate:

  • the LINQ query specifies how to locate the values and

  • a loop accesses the results

making the code easier to understand and maintain.

9.2.5 The orderby Clause

The orderby clause (line 36) sorts the query results in ascending order. Lines 49 and 64 use the descending modifier in the orderby clause to sort the results in descending order. An ascending modifier also exists but isn’t normally used, because it’s the default. Any value that can be compared with other values of the same type may be used with the or-derby clause. A value of a simple type (e.g., int) can always be compared to another value of the same type; we’ll discuss how to compare values of reference types in Chapter 12.

The queries in lines 48–50 and 62–65 generate the same results, but in different ways. Lines 48–50 uses LINQ to sort the results of the query filtered from lines 22–24. The second query uses both the where and orderby clauses. Because queries can operate on the results of other queries, it’s possible to build a query one step at a time, passing the results of queries between methods for further processing.

9.2.6 Interface IEnumerable<T>

As we mentioned, the foreach statement can iterate through the contents of arrays, collections and LINQ query results. Actually, foreach iterates over any so-called IEnumerable<T> object, which just happens to be what most LINQ queries return.

IEnumerable<T> is an interface. Interfaces define and standardize the ways in which people and systems can interact with one another. For example, the controls on a radio serve as an interface between radio users and the radio’s internal components. The controls allow users to perform a limited set of operations (e.g., changing the station, adjusting the volume, and choosing between AM and FM), and different radios may implement the controls in different ways (e.g., using push buttons, dials or voice commands). The interface specifies what operations a radio permits users to perform but does not specify how the operations are implemented. Similarly, the interface between a driver and a car with a manual transmission includes the steering wheel, the gear shift, the clutch, the gas pedal and the brake pedal. This same interface is found in nearly all manual-transmission cars, enabling someone who knows how to drive one manual-transmission car to drive another.

Software objects also communicate via interfaces. A C# interface describes a set of methods and properties that can be called on an object—to tell the object, for example, to perform some task or return some piece of information. The IEnumerable<T> interface describes the functionality of any object that can be iterated over and thus offers methods and properties to access each element. A class that implements an interface must declare all the methods and properties described by that interface.

Most LINQ queries return an IEnumerable<T> object—some queries return a single value (e.g., the sum of an int array’s elements). For queries that return an IEnumerable<T> object, you can use a foreach statement to iterate over the query results. The notation <T> indicates that the interface is a generic interface that can be used with any type of data (for example, ints, strings or Employees). You’ll learn more about the <T> notation in Section 9.4. In Section 12.7, we’ll discuss interfaces and show how to define your own interfaces. In Chapter 20, we’ll cover generics in detail.

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

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