How do ranges work in Ruby?
Short answer
1..5 includes 5; 1...5 stops at 4. Ranges are enumerable, so every Enumerable method works on them, and they can be used for slicing and in case expressions.
(1..5).to_a # [1, 2, 3, 4, 5]
(1...5).to_a # [1, 2, 3, 4]
('a'..'e').to_a # ["a", "b", "c", "d", "e"]The three-dot form is easy to misread. Many Ruby developers avoid it except where excluding the end is clearly the point, such as 0...array.length.
#As iterators
(1..5).each { |i| puts i }
(1..5).map { |i| i * i }
(1..100).select(&:even?).sum#For slicing
letters = %w[a b c d e]
letters[1..3] # ["b", "c", "d"]
letters[1...3] # ["b", "c"]
letters[2..] # ["c", "d", "e"] — endless range
letters[..2] # ["a", "b", "c"] — beginless range
letters[-2..] # ["d", "e"]#In case expressions
grade = case score
when 90.. then "A"
when 80...90 then "B"
when 70...80 then "C"
else "Keep going"
endcase uses ===, and for a range that means "does it cover this value". This is the cleanest way to write a banded lookup in Ruby.
#Membership
(1..10).include?(5) # true — for ranges of any type
(1..10).cover?(5) # true — faster, compares endpoints onlyUse cover? for numbers and dates. include? may iterate the whole range, which is slow on a large one and impossible on an endless one.
#Infinite ranges are lazy
(1..).lazy.map { |n| n * 2 }.first(5) # [2, 4, 6, 8, 10]Without .lazy that would never finish.