Ranges
A range is two endpoints and a direction. 1..5 is inclusive; 1...5 excludes the end. Ranges work as iterators, as case-statement matchers, and as slice indices.
Range patterns
EXAMPLE
# Build
r1 = 1..5 # 1, 2, 3, 4, 5 — inclusive
r2 = 1...5 # 1, 2, 3, 4 — exclusive end
letters = 'a'..'e' # ranges work on anything that implements <=> + succ
# Iterate
(1..5).each { |i| puts i }
(1...5).map { |i| i * i } # [1, 4, 9, 16]
(1..1000).step(2).to_a # odd numbers 1..999
# Test membership
(1..10).cover?(5) # true — O(1) on integer ranges
(1..10).include?(5) # true — O(n) on arbitrary ranges
('a'..'z').cover?('f')
# Case / when with ranges
case age
when 0..12 then puts 'kid'
when 13..17 then puts 'teen'
when 18..64 then puts 'adult'
else puts 'senior'
end
# Slice arrays / strings
[10, 20, 30, 40, 50][1..3] # [20, 30, 40]
[10, 20, 30, 40, 50][1...3] # [20, 30]
'hello world'[0..4] # 'hello'
# Endless / beginless (Ruby 2.6+)
arr[2..] # everything from index 2 onwards
arr[..3] # everything up to index 3 inclusive
# Sample / random
(1..100).to_a.sample # random integer 1..100
rand(1..100) # same, idiomatic
# Lazy — for huge ranges
(1..Float::INFINITY).lazy.map { |n| n * n }.first(5) # [1, 4, 9, 16, 25]
Why it matters
Range + case is the most readable way to bucket numeric input. Reach for it whenever you find yourself writing elsif x < N && x >= M chains.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…