Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if two ranges overlap in ruby

Tags:

ruby

I know that I can do:

(1..30).cover?(2)
=> true

But when I try to do the same with another range it always returns false:

(1..30).cover?(2..3)
=> false

So my question is - is there any elegant way to compare two ranges in ruby? In my case I want to check if two DateTime-ranges overlap. Thanks in advance.

like image 919
Peter Piper Avatar asked Oct 08 '16 15:10

Peter Piper


People also ask

How can you tell if two date ranges overlap?

You can do this by swapping the ranges if necessary up front. Then, you can detect overlap if the second range start is: less than or equal to the first range end (if ranges are inclusive, containing both the start and end times); or. less than (if ranges are inclusive of start and exclusive of end).

What is range overlap?

If both ranges have at least one common point, then we say that they're overlapping. In other words, we say that two ranges and are overlapping if: On the other hand, non-overlapping ranges don't have any points in common.

How do you calculate overlap time?

Overlap = min(A2, B2) - max(A1, B1) + 1. In other words, the overlap of two integer intervals is a difference between the minimum value of the two upper boundaries and the maximum value of the two lower boundaries, plus 1.

How does SQL determine overlapping data?

SQL Query using Lag Function for OverLapping Time Intervals SQL programmers now can use SQL LAG() function to compare all table rows with the previous row when ordered by ID column.


2 Answers

Two ranges overlap for a given range A when:

  1. range B starts within range A,
  2. range B ends within range A or
  3. range B starts before range A and ends after range A

Examples:

Range A    |-----|
             |-----|  Case 1
         |-----|      Case 2
             |-|      Case 1 + 2
         |---------|  Case 3

Looking closer the rule is: Two ranges overlap when Range B starts before the range A ends and range B ends after the range A starts.

def ranges_overlap?(range_a, range_b)
  range_b.begin <= range_a.end && range_a.begin <= range_b.end 
end 
like image 57
spickermann Avatar answered Nov 09 '22 18:11

spickermann


def overlap?(r1,r2)
  !(r1.first > r2.last || r1.last < r2.first)
end

overlap? 1..5, 4..10 #=> true
overlap? 1..5, 6..10 #=> false
overlap? 1..10, 4..8 #=> true
overlap? 1..4, 4..8  #=> true

The operative line is equivalent to:

r1.first <= r2.last && r1.last >= r2.first

I normally try to avoid negation, but in this case I think it reads better with it.

Another way:

def overlap?(r1,r2)
  !(([r1.first, r2.first].min..[r1.last, r2.last].max).size >= r1.size + r2.size)
end

overlap? 1..5, 4..10 #=> true
overlap? 1..5, 6..10 #=> false
overlap? 1..10, 4..8 #=> true
overlap? 1..4, 4..8  #=> true

The operative line is equivalent to:

([r1.first, r2.first].min..[r1.last, r2.last].max).size < r1.size + r2.size

Again, I prefer the one with negation.

like image 37
Cary Swoveland Avatar answered Nov 09 '22 18:11

Cary Swoveland