Ranges with 2 or 3 dots
Ruby provides a convenient way to represent sequences of values using Ranges. These can be created using either two dots (..
) or three dots (...
), and while both create valid Range objects, they behave differently in important ways.
Basic Range Syntax
Both notations create valid Range objects in Ruby:
(1..5).class # => Range
(1...5).class # => Range
("a".."z").class # => Range
("a"..."z").class # => Range
The Key Difference: Inclusivity
The fundamental difference between these notations lies in how they handle the end value:
- Two dots (
..
) creates an inclusive range that includes both the start and end values - Three dots (
...
) creates an exclusive range that includes the start value but excludes the end value
Pretty neat. Here is an example of it in action:
(1..10).to_a
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1...10).to_a
# => [1, 2, 3, 4, 5, 6, 7, 8, 9]
("a".."j").to_a
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
("a"..."j").to_a
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
So there are a number of ways you can use this. Like how you access the data in an array:
array = ['a', 'b', 'c', 'd', 'e']
array[0..2] # => ["a", "b", "c"]
array[0...2] # => ["a", "b"]
Or how many numbers you iterate through:
# Print numbers 1 through 5
(1..5).each { |n| puts n } # Prints 1, 2, 3, 4, 5
# Print numbers 1 through 4
(1...5).each { |n| puts n } # Prints 1, 2, 3, 4
Date ranges are a big and useful one.
require 'date'
# Inclusive date range (includes both Jan 1 and Jan 31)
(Date.new(2024,1,1)..Date.new(2024,1,31))
# Exclusive date range (excludes Jan 31)
(Date.new(2024,1,1)...Date.new(2024,1,31))
Recap
- Use
..
when you want to include the end value (e.g., counting from 1 to 10 inclusive) - Use
...
when you want to exclude the end value (e.g., array slicing or zero-based counting)