Ruby Ranges
Ranges are a unique and powerful feature in Ruby, used to represent a continuous sequence of values. Ranges can include numbers, letters, and even more complex objects. Ruby ranges are feature-rich, supporting various operations and iteration methods. This chapter will introduce in detail the creation, operations, and usage methods of ranges in Ruby.
🎯 Range Basics
Range Definition
Ruby provides two ways to create ranges:
ruby
# Range including end value (..)
inclusive_range = 1..5
puts inclusive_range # 1..5
puts inclusive_range.include?(5) # true
# Range excluding end value (...)
exclusive_range = 1...5
puts exclusive_range # 1...5
puts exclusive_range.include?(5) # false
# Character range
letter_range = 'a'..'e'
puts letter_range # "a".."e"
puts letter_range.include?('e') # true
# String range
word_range = 'aa'..'az'
puts word_range # "aa".."az"
# Date range
require 'date'
date_range = Date.new(2023, 12, 25)..Date.new(2023, 12, 31)
puts date_range # 2023-12-25..2023-12-31Range Creation Methods
ruby
# Use literal syntax (recommended)
range1 = 1..10
range2 = 'a'..'z'
# Use Range.new method
range3 = Range.new(1, 10)
range4 = Range.new('a', 'z')
range5 = Range.new(1, 10, true) # Third parameter true means exclude end value
puts range1 == range3 # true
puts range2 == range4 # true
puts range5 # 1...10📏 Range Properties
Range Basic Properties
ruby
range = 1..10
# Get range start and end values
puts range.begin # 1
puts range.first # 1 (alias for begin)
puts range.end # 10
puts range.last # 10 (alias for end)
# Check if end value is excluded
puts range.exclude_end? # false
puts (1...10).exclude_end? # true
# Range length
puts range.size # 10
# Check if range is empty
puts range.empty? # false
puts (5..3).empty? # true (invalid range)
# Convert to array
array = range.to_a
puts array.inspect # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]Range Comparison
ruby
range1 = 1..5
range2 = 1..5
range3 = 2..6
# Equality comparison
puts range1 == range2 # true
puts range1 == range3 # false
# Range coverage
puts range1.cover?(3) # true
puts range1.cover?(6) # false
puts range1.include?(3) # true
puts range1.include?(6) # false
# Use === operator (used in case statements)
puts range1 === 3 # true
puts range1 === 6 # false
# Range coverage check
range_a = 1..10
range_b = 3..7
range_c = 8..15
puts range_a.cover?(range_b) # true (range_a contains range_b)
puts range_a.cover?(range_c) # false🔍 Range Operations
Range Member Check
ruby
numbers = 1..100
# Check single value
puts numbers.include?(50) # true
puts numbers.include?(150) # false
# Use cover? method (more efficient)
puts numbers.cover?(50) # true
puts numbers.cover?(150) # false
# Check another range
small_range = 25..75
puts numbers.cover?(small_range) # true
# Character range check
letters = 'a'..'z'
puts letters.include?('m') # true
puts letters.include?('A') # false
# String range check
words = 'aa'..'zz'
puts words.include?('ab') # true
puts words.include?('ba') # true
puts words.include?('aaa') # false (out of range)Range Boundary Operations
ruby
range = 10..20
# Get min and max values of range
puts range.min # 10
puts range.max # 20
# Get first n elements
puts range.first(3).inspect # [10, 11, 12]
# Get last n elements
puts range.last(3).inspect # [18, 19, 20]
# Get median value of range
def range_median(range)
min, max = range.minmax
(min + max) / 2.0
end
puts range_median(range) # 15.0
# Range statistics
def range_stats(range)
{
min: range.min,
max: range.max,
size: range.size,
average: (range.min + range.max) / 2.0
}
end
stats = range_stats(1..100)
puts stats # {:min=>1, :max=>100, :size=>100, :average=>50.5}🔁 Range Iteration
Basic Iteration
ruby
# Numeric range iteration
(1..5).each { |n| print "#{n} " }
puts # 1 2 3 4 5
# Character range iteration
('a'..'e').each { |char| print "#{char} " }
puts # a b c d e
# Use each_with_index
('x'..'z').each_with_index do |char, index|
puts "#{index}: #{char}"
end
# 0: x
# 1: y
# 2: z
# Reverse iteration
(1..5).reverse_each { |n| print "#{n} " }
puts # 5 4 3 2 1Advanced Iteration
ruby
# Use map for transformation
squared = (1..5).map { |n| n ** 2 }
puts squared.inspect # [1, 4, 9, 16, 25]
# Use select for filtering
evens = (1..10).select(&:even?)
puts evens.inspect # [2, 4, 6, 8, 10]
# Use reject for exclusion
odds = (1..10).reject(&:even?)
puts odds.inspect # [1, 2, 3, 5, 7, 9]
# Use find for searching
first_large = (1..100).find { |n| n > 50 }
puts first_large # 51
# Use take and drop
range = 1..20
puts range.take(5).inspect # [1, 2, 3, 4, 5]
puts range.drop(15).inspect # [16, 17, 18, 19, 20]Chunk Iteration
ruby
# Use each_slice for chunked processing
(1..20).each_slice(5) do |slice|
puts slice.inspect
end
# [1, 2, 3, 4, 5]
# [6, 7, 8, 9, 10]
# [11, 12, 13, 14, 15]
# [16, 17, 18, 19, 20]
# Use each_cons for consecutive processing
(1..10).each_cons(3) do |cons|
puts cons.inspect
end
# [1, 2, 3]
# [2, 3, 4]
# [3, 4, 5]
# [4, 5, 6]
# [5, 6, 7]
# [6, 7, 8]
# [7, 8, 9]
# [8, 9, 10]
# Use cycle for infinite looping
# (1..3).cycle(2) { |n| print "#{n} " }
# puts # 1 2 3 1 2 3🎯 Range Practice Examples
Number Range Applications
ruby
class NumberRangeUtils
# Check if number is within valid range
def self.valid_range?(number, min, max)
(min..max).cover?(number)
end
# Generate random number within range
def self.random_in_range(range)
range.to_a.sample
end
# Calculate primes within range
def self.primes_in_range(range)
range.select { |n| prime?(n) }
end
# Sum of numbers in range
def self.sum_range(range)
# For large ranges, mathematical formula is more efficient
if range.exclude_end?
n = range.last - range.first
else
n = range.last - range.first + 1
end
n * (range.first + range.last) / 2
end
private
def self.prime?(n)
return false if n < 2
return true if n == 2
return false if n.even?
(3..Math.sqrt(n)).step(2) do |i|
return false if n % i == 0
end
true
end
end
# Use number range tools
puts NumberRangeUtils.valid_range?(15, 10, 20) # true
puts NumberRangeUtils.valid_range?(25, 10, 20) # false
random_number = NumberRangeUtils.random_in_range(1..100)
puts "Random number: #{random_number}"
primes = NumberRangeUtils.primes_in_range(1..30)
puts "Primes from 1 to 30: #{primes.inspect}" # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
sum = NumberRangeUtils.sum_range(1..100)
puts "Sum from 1 to 100: #{sum}" # 5050Character Range Applications
ruby
class CharacterRangeUtils
# Generate alphabet
def self.alphabet
('a'..'z').to_a
end
# Generate uppercase alphabet
def self.uppercase_alphabet
('A'..'Z').to_a
end
# Check if character is a letter
def self.letter?(char)
('a'..'z').cover?(char.downcase)
end
# Check if character is a digit
def self.digit?(char)
('0'..'9').cover?(char)
end
# Generate random letter
def self.random_letter
('a'..'z').to_a.sample
end
# Generate random string within range
def self.random_string(length, char_range = 'a'..'z')
Array.new(length) { char_range.to_a.sample }.join
end
# String range check
def self.in_range?(string, start_str, end_str)
(start_str..end_str).cover?(string)
end
end
# Use character range tools
puts "Alphabet: #{CharacterRangeUtils.alphabet.take(5).inspect}" # ["a", "b", "c", "d", "e"]
puts "Uppercase: #{CharacterRangeUtils.uppercase_alphabet.take(5).inspect}" # ["A", "B", "C", "D", "E"]
puts CharacterRangeUtils.letter?('A') # true
puts CharacterRangeUtils.digit?('5') # true
random_letter = CharacterRangeUtils.random_letter
puts "Random letter: #{random_letter}"
random_string = CharacterRangeUtils.random_string(8)
puts "Random string: #{random_string}"
puts CharacterRangeUtils.in_range?("apple", "a", "z") # trueDate Range Applications
ruby
require 'date'
class DateRangeUtils
# Create date range
def self.date_range(start_date, end_date)
Date.parse(start_date)..Date.parse(end_date)
end
# Calculate days in date range
def self.days_in_range(date_range)
(date_range.end - date_range.begin).to_i + 1
end
# Get business days count
def self.business_days(date_range)
count = 0
date_range.each { |date| count += 1 unless date.saturday? || date.sunday? }
count
end
# Get weekend days count
def self.weekend_days(date_range)
count = 0
date_range.each { |date| count += 1 if date.saturday? || date.sunday? }
count
end
# Group by week
def self.group_by_week(date_range)
weeks = Hash.new { |h, k| h[k] = [] }
date_range.each do |date|
week_number = date.cweek
weeks[week_number] << date
end
weeks
end
# Get dates for specific weekday
def self.days_of_week(date_range, weekday)
# 0 = Sunday, 1 = Monday, ..., 6 = Saturday
date_range.select { |date| date.wday == weekday }
end
end
# Use date range tools
date_range = DateRangeUtils.date_range("2023-12-01", "2023-12-31")
puts "Days in December: #{DateRangeUtils.days_in_range(date_range)}" # 31
business_days = DateRangeUtils.business_days(date_range)
puts "Business days: #{business_days}"
weekend_days = DateRangeUtils.weekend_days(date_range)
puts "Weekend days: #{weekend_days}"
mondays = DateRangeUtils.days_of_week(date_range, 1)
puts "Mondays in December: #{mondays.inspect}"🔧 Range with Other Data Structures
Range and Array
ruby
# Range to array
numbers = (1..10).to_a
puts numbers.inspect # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Array to range (if possible)
def array_to_range(arr)
return nil if arr.empty?
min, max = arr.minmax
(min..max) if (min..max).to_a == arr.sort
end
consecutive = [1, 2, 3, 4, 5]
puts array_to_range(consecutive) # 1..5
non_consecutive = [1, 3, 5, 7]
puts array_to_range(non_consecutive) # nil
# Range and array operations
range = 1..10
array = [5, 15, 25, 35]
# Check if array elements are within range
in_range = array.select { |n| range.cover?(n) }
puts in_range.inspect # [5]
# Find array elements outside range
out_of_range = array.reject { |n| range.cover?(n) }
puts out_of_range.inspect # [15, 25, 35]Range and Hash
ruby
# Use range as hash keys
grade_ranges = {
90..100 => "A",
80...90 => "B",
70...80 => "C",
60...70 => "D",
0...60 => "F"
}
def get_grade(score, grade_ranges)
grade_ranges.each do |range, grade|
return grade if range.cover?(score)
end
"Invalid"
end
puts get_grade(95, grade_ranges) # A
puts get_grade(85, grade_ranges) # B
puts get_grade(75, grade_ranges) # C
puts get_grade(65, grade_ranges) # D
puts get_grade(55, grade_ranges) # F
# Group data by range
scores = [95, 87, 76, 92, 88, 73, 91, 85, 79, 94]
score_groups = scores.group_by do |score|
case score
when 90..100 then "Excellent"
when 80...90 then "Good"
when 70...80 then "Average"
when 60...70 then "Pass"
else "Fail"
end
end
puts score_groups
# {"Excellent"=>[95, 92, 91, 94], "Good"=>[87, 88, 85], "Average"=>[76, 73, 79]}📊 Range Performance Optimization
Large Range Processing
ruby
# For large ranges, avoid converting to array
# Inefficient way
# sum = (1..1_000_000).to_a.sum
# Efficient way
def sum_range_efficiently(range)
first, last = range.first, range.last
count = last - first + 1
count * (first + last) / 2
end
large_sum = sum_range_efficiently(1..1_000_000)
puts "Large range sum: #{large_sum}" # 500000500000
# Use lazy for deferred computation
def process_large_range(range)
range.lazy
.select(&:even?)
.map { |n| n ** 2 }
.first(10)
.to_a
end
result = process_large_range(1..1_000_000)
puts result.inspect # [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]Range Search Optimization
ruby
# Use bsearch for binary search (requires sorted range)
sorted_array = (1..1000).to_a
# Find first element >= 500
index = sorted_array.bsearch_index { |x| x >= 500 }
puts "Index: #{index}, Value: #{sorted_array[index]}" # Index: 499, Value: 500
# Find last element <= 500
index = sorted_array.bsearch_index { |x| x > 500 }&.pred || (sorted_array.length - 1)
puts "Index: #{index}, Value: #{sorted_array[index]}" # Index: 499, Value: 500
# Efficient search within range
class RangeSearch
def self.binary_search(range, target)
return false unless range.cover?(target)
low, high = range.first, range.last
while low <= high
mid = (low + high) / 2
return true if mid == target
if mid < target
low = mid + 1
else
high = mid - 1
end
end
false
end
end
puts RangeSearch.binary_search(1..1000, 500) # true
puts RangeSearch.binary_search(1..1000, 1500) # false🎯 Range Best Practices
1. Choose Appropriate Range Type
ruby
# Range including end value (for most cases)
inclusive_range = 1..10
puts inclusive_range.to_a # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Range excluding end value (for array indices, etc.)
exclusive_range = 0...10
puts exclusive_range.to_a # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Use in loops
array = ['a', 'b', 'c', 'd', 'e']
(0...array.length).each { |i| puts "#{i}: #{array[i]}" }
# Use in case statements
def grade_letter(score)
case score
when 90..100 then 'A'
when 80...90 then 'B'
when 70...80 then 'C'
when 60...70 then 'D'
else 'F'
end
end
puts grade_letter(95) # A
puts grade_letter(85) # B2. Safely Handle Ranges
ruby
class SafeRangeHandler
# Safely create range
def self.safe_range(start_val, end_val, exclude_end = false)
return nil if start_val.nil? || end_val.nil?
return nil if start_val > end_val
exclude_end ? (start_val...end_val) : (start_val..end_val)
end
# Safely iterate range
def self.safe_each(range, &block)
return [] unless range.is_a?(Range)
return [] if range.empty?
range.each(&block) if block_given?
range.to_a
end
# Validate range bounds
def self.validate_range(range, min_bound, max_bound)
return false unless range.is_a?(Range)
range.begin >= min_bound && range.end <= max_bound
end
end
# Use safe range handling
safe_range = SafeRangeHandler.safe_range(1, 10)
puts safe_range # 1..10
invalid_range = SafeRangeHandler.safe_range(10, 1)
puts invalid_range # nil
SafeRangeHandler.safe_each(1..5) { |n| puts "Number: #{n}" }
valid = SafeRangeHandler.validate_range(1..10, 0, 100)
puts "Range valid: #{valid}" # Range valid: true3. Range Performance Optimization
ruby
class RangePerformance
# Efficient range sum
def self.sum_range(range)
# Use mathematical formula instead of iteration
n = range.size
first, last = range.first, range.last
n * (first + last) / 2
end
# Efficient range average
def self.average_range(range)
(range.first + range.last) / 2.0
end
# Efficient range median
def self.median_range(range)
min, max = range.minmax
(min + max) / 2.0
end
# Fast contains check (use cover? instead of include?)
def self.fast_contains?(range, value)
range.cover?(value)
end
end
# Use performance-optimized range operations
large_range = 1..1_000_000
puts "Sum: #{RangePerformance.sum_range(large_range)}" # 500000500000
puts "Average: #{RangePerformance.average_range(large_range)}" # 500000.5
puts "Median: #{RangePerformance.median_range(large_range)}" # 500000.5
puts RangePerformance.fast_contains?(1..100, 50) # true📚 Next Steps
After mastering Ruby range operations, it is recommended to continue learning:
- Ruby Iterators - Deep dive into iteration patterns
- Ruby File Processing and I/O - Learn file read/write operations
- Ruby Exception Handling - Master error handling mechanisms
- Ruby Object-Oriented Programming - Deep understanding of OOP
Continue your Ruby learning journey!