Microsoft & .NET.NETWorking with Range Variables and Let Statements in LINQ

Working with Range Variables and Let Statements in LINQ

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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.

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.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories