A range is like a variation of an array, one that’s sequential and much, much easier to create. Ranges use the same naming scheme as arrays and other variables and are assigned values using the assignment operator. But the value of a range is created by separating the beginning and end points of the range by either two or three periods:
numbers = 1...10 letters = 'A'..'Z'
If two dots are used, the range includes both the starting and ending values. If three dots are used, the range includes the starting value but not the ending value (so the numbers range represents the integers 1 through 9).
One interesting thing about a range is that it’s not actually populated like an array. If you were to output a range, you’d see just how it was created (Figure 4.16):
puts numbers puts letters
Figure 4.16 Calling puts with ranges prints out their definition, not the values they implicitly contain.
One way to make a range more useful is by applying the to_a method to convert it to an array (Figure 4.17):
Figure 4.17 The values within a range can be printed (or used in other ways) by converting it to an array first.
To see if a value is found within a range, use the include? method:
numbers.include?(9) # true letters.include?('B') # true letters.include?('b') # false
Ranges are most often used for comparisons (e.g., to see if a number is within a certain series) or iterations (e.g., looping through something ten times). You can create ranges using different data types, but in reality you’ll only want to do so with numbers and simple strings. The reason why is quite technical, but it has to do with Ruby’s ability to sequence the values. For example, it’s easy to make a range from A to Z, to check if a letter is within that range, and to print out each character in the range. The same goes for integers. And Ruby can easily check if a float is within a given range:
gpa = 0.0..4.0 gpa.include?(3.2) # true
However, Ruby could not convert that floating-point range to an array and iterate through it because there’d be a virtually infinite list of values.
Two final thoughts on ranges before walking through some examples: First, ranges can be used without variables, but doing so requires wrapping the range within parentheses:
Second, ranges can be used to indicate a subset of an array:
a = [1, 2, 3, 4, 5, 6] puts a[0..2]
The puts line will print the numbers 1, 2, and 3, which are the elements indexed at 0, 1, and 2).
To use ranges:
See if a year is within a valid range (Figure 4.18):
puts (1900..2008).include?(1945) puts (1900..2008).include?(2010)
Figure 4.18 One common use of ranges is to see if a value falls within an allowed set.
A year is just an integer, so it can easily be checked against a range of given values.
Create a string containing every lowercase letter in reverse order (Figure 4.19):
letters = ('a'..'z').to_a.reverse.join
Figure 4.19 By creating a range and converting it to an array, it can easily be manipulated, including turning it into a string.
The range 'a'..'z' will create the necessary 26 letters (you couldn’t use 'z'..'a', as ranges must use incremental values). This range is then converted to an array, placed in reverse order, and then turned into a string using join.
Find the next five letters starting at the middle of the alphabet (Figure 4.20):
letters = ('a'..'z').to_a puts letters[13..17]
Figure 4.20 Here ranges are used both to create the initial array and to find a subset of it.
Alternatively, you could write this all on one line:
puts ('a'..'z').to_a[13, 5]
(Did I mention there’s more than one way to do things in Ruby?)