Reputation: 12066
What is the most beautiful way to break a larger range into smaller non overlapping ranges?
range = 1..375
Desired Output:
1..100
101..200
201..300
301..375
Upvotes: 5
Views: 419
Reputation: 12066
Currently, I'm using the step
method, but I don't like having to check the top of the range and do calculations to avoid overlapping:
For example:
range = 1..375
interval = 100
range.step(interval).each do |start|
stop = [range.last, start + (interval - 1)].min
puts "#{start}..#{stop}"
end
I've taken this code and extended Range
as well:
class Range
def in_sub_ranges(interval)
step(interval).each do |start|
stop = [range.last, start + (interval - 1)].min
yield(start..stop)
end
end
end
This allows me to do
range.in_sub_ranges(100) { |sub| puts sub }
Upvotes: 0
Reputation: 110675
The following may not be the most elegant solution but it is designed to be relatively efficient, by avoiding the creation of temporary arrays.
def divide_range(range, sz)
start = range.begin
(range.size/sz).times.with_object([]) do |_,arr|
arr << (start..start+sz-1)
start += sz
end.tap { |arr| (arr << (start..range.end)) if start < range.end }
end
divide_range(1..375, 100)
#=> [1..100, 101..200, 201..300, 301..375]
divide_range(1..400, 100)
#=> [1..100, 101..200, 201..300, 301..400]
divide_range(50..420, 50)
#=> [50..99, 100..149, 150..199, 200..249, 250..299, 300..349,
# 350..399, 400..420]
n = 1_000_000_000_000
divide_range(1..n, n/2)
#=> [1..500000000000, 500000000001..1000000000000]
Upvotes: 2
Reputation: 2773
You can use #each_slice
in combination with #map
:
(1..375).each_slice(100).map { |a,*,b| (a..b) }
#=> [1..100, 101..200, 201..300, 301..375]
Upvotes: 7