Working with Range Variables and Let Statements in LINQ

Introduction

Complicated things seem intuitively simple when complexity is cleverly hidden in the open. LINQ is a query language that at its basic is pretty simple to learn to use, but there is a lot of meaning in all of the aspects of a query. Understanding these cleverly hidden meanings will help you get beyond the basics and really unleash the power of LINQ.

Working with range and let introduces the notion of range and temporary range variables. Understanding how range variables are defined in from and let statements in LINQ will help you write more powerful queries.

Using Range Variables

The From clause is the first clause in a LINQ query, everywhere it's required. From is always required except in Aggregate queries. As a refresher, a basic LINQ query is

From something In someCollection Select something

The From clause is First because it defines the context for Intellisense, which is used to aid the Select clause, especially if you define a projection. (A projection is a new type that can be defined using "with new". If you follow the grammar of the syntax advantage, you can write a LINQ query. The sample in Listing 1 uses an initialized array of integers, and the query returns only the even numbers.

Listing 1: A basic query that returns even numbers.

Sub Range()
   Dim numbers = New Integer() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

   Dim evens = From num In numbers _
      Where num Mod 2 = 0 _
      Select num

   For Each value In evens
      Console.WriteLine(value)
   Next
   Console.ReadLine()
End Sub

The LINQ query is the statement that begins with Dim evens = From num In numbers. The first word after the From keyword is referred to as the range variable. The Range variable plays the same role as the iteration variable in a for loop. For example,

For Each num In numbers

illustrates the relationship between a range variable in a LINQ query and the iteration variable in a for loop.

In the Select clause, you can select just the range variable or if the range variable is a compound type—read an instance of an object—you can define a projection using Select New With. The next section is an unrelated use of the word Range but it does demonstrate a projection in the Select clause.

Unrelated: Using Range Generation

In object-oriented land, there are a lot of synonymous meanings. Generalization in UML means inheritance in code. There are also words that are used as methods. For instance, I talked about the first word after From being called a range. There is a Range method, too. In this case, the words are similar but not related in this context. The Enumerable.Range method will create a Range for you.

Although the Range method is not directly related to the concept of a range for LINQ queries, it can be used here to generate a range to demonstrate how to create a projection. A projection is an anonymous type derived dynamically from elements of existing types. In Listing 2, the Enumerable.Range method is used to create a range of integers from 33 to 42. The first argument is the starting point and the second argument is the count. In the listing, the LINQ statement queries the dynamic range of integers from 33 to 42 and uses the projection syntax to create a new type that is an object containing the number and the word Even or Odd indicating the word's parity (or evenness or oddness).

Listing 2: Using the Enumerable.Range method to generate a dynamic range and a projection to create a new type containing the number and the word's parity.

Sub RangeMethod()

   Dim numbers = From num In Enumerable.Range(33, 10) _
      Select New With {.Number = num, .Parity = _
      IIf(num Mod 2 = 0, "Even", "Odd")}

   For Each value In numbers
      Console.WriteLine(value)
   Next
   Console.ReadLine()

Sub

A projection is created with the Select New With clause. New fields can be defined with the .Name syntax and the values can be assigned from the range variable or dynamically created. In the example, the .Number field is mapped directly to the range variable and the IIf function tests the range value for evenness—num Mod 2 = 0—and labels the .Parity field with the string "Even" or "Odd" based on the result of the first argument, the test statement.

Working with Range Variables and Let Statements in LINQ

Creating Temporary Range Variables with Let

Some parts of a query, such as embedded or nested queries, require a bit of extra effort to define. In some queries, it might be helpful to use the results of the query in more than one location. To repeat the query literally would mean that the whole query itself might get quickly out of hand in terms of length, and longer queries are harder to write, harder to debug, and harder to read. To mitigate this problem, LINQ introduces the Let clause. Let permits you to introduce and initialize temporary variables within a LINQ statement and instead of repeating the elements needed to reproduce the result, you can use the temporary variable. In some queries, Let is key to making the query manageable.

Listing 3 contains an array of strings, a quote from Samuel Clemens. The LINQ query parses the array of strings into the words sans the spaces and punctuation (maybe the kind of capability needed in a spell checker). The From clause defines a range, twain, that represents each of the strings in the array. The Let clause parses each string into the individual words within the string, eliminating the spaces and punctuation. This clause defines the range words:

Let words = twain.Split(New Char() {".", " "}, _
   StringSplitOptions.RemoveEmptyEntries) _

Having defined the range words, you can use these words within the same query. In the example, it is the words you want, not the whole substring. The result of the query is a sequence containing the individual words only.

Listing 3: Using the Let clause to define a temporary range that the query can reuse.

Sub LetRange()
   Dim markTwain = New String() _
      {"Clothes make the man. Naked people", _
       "have little or no influence in society."}


   Dim noInfluence = From twain In markTwain _
      Let words = twain.Split(New Char() {".", " "}, _
         StringSplitOptions.RemoveEmptyEntries) _
      From word In words _
      Select word

   For Each w In noInfluence
      Console.WriteLine(w)
   Next
   Console.ReadLine()

End Sub

Listing 4 shows how you can reuse the temporary range variable multiple times in tehe same query. In this revision, all of the items in words are converted to all uppercase letters.

Listing 4: Using the temproary range variable multiple times in the same query.

Dim noInfluence = From twain In markTwain _
   Let words = twain.Split(New Char() {".", " "}, _
      StringSplitOptions.RemoveEmptyEntries) _
   From word In words _
   Let upper = word.ToUpper() _
   Select upper

Summary

Just know that LINQ is a masterful work of software engineering, and I hope that understanding ranges a little better will help you make better use of this cool new technology.

Biography

Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his upcoming book, LINQ Unleashed for C#, due in Spring 2008. You may contact him for technology questions at pkimmel@softconcepts.com.

If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org. Glugnet opened a users group branch in Flint, Michigan in August 2007. If you are interested in attending, check out the www.glugnet.org web site for updates.

Copyright © 2008 by Paul T. Kimmel. All Rights Reserved.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds