I have an array with several time ranges inside:
[Tue, 24 May 2011 08:00:00 CEST +02:00..Tue, 24 May 2011 13:00:00 CEST +02:00,
Tue, 24 May 2011 16:30:00 CE
Dont you just want to find the smallest first value and the largest last value from the set of arrays?
ranges = [Tue, 24 May 2011 08:00:00 CEST +02:00..Tue, 24 May 2011 13:00:00 CEST +02:00,
Tue, 24 May 2011 16:30:00 CEST +02:00..Tue, 24 May 2011 18:00:00 CEST +02:00,
Tue, 24 May 2011 08:00:00 CEST +02:00..Tue, 24 May 2011 09:00:00 CEST +02:00,
Tue, 24 May 2011 15:30:00 CEST +02:00..Tue, 24 May 2011 18:00:00 CEST +02:00]
union = [ranges.collect(&:first).sort.first, ranges.collect(&:last).sort.last]
The gem range_operators does a wonderful job by adding missing features to the Ruby Range
class. It is way smaller than adding the whole facets gem.
I your case the solution would be the rangify
method, which is added to the Array
class and would do exactly what you are looking for.
Given a function that returns truthy if two ranges overlap:
def ranges_overlap?(a, b)
a.include?(b.begin) || b.include?(a.begin)
end
(this function courtesy of sepp2k and steenslag)
and a function that merges two overlapping ranges:
def merge_ranges(a, b)
[a.begin, b.begin].min..[a.end, b.end].max
end
then this function, given an array of ranges, returns a new array with any overlapping ranges merged:
def merge_overlapping_ranges(overlapping_ranges)
overlapping_ranges.sort_by(&:begin).inject([]) do |ranges, range|
if !ranges.empty? && ranges_overlap?(ranges.last, range)
ranges[0...-1] + [merge_ranges(ranges.last, range)]
else
ranges + [range]
end
end
end
Some kind of algorithm that might help:
Sort range array by start time (r1, r2, r3, r4, .. rn)
for each range pair [r1, r2], [r2, r3] .. [rn-1, rn]:
if r1_end > r2_start: # they overlap
add [r1_start, r2_end] to new range array
else: # they do not overlap
add [r1] and [r2] to new range array (no changes)
startover with the new range array until no more changes