I want to input a list of intervals and check the intervals of the union of overlapping intervals and the intervals of non-overlapping intervals

Question:

For example: I have the intervals = [[-5,-3],[-4,-1],[1,3],[4,8],[5,10],[10,12], [15,20]] (not necessaraly sorted like that)

I want the function to return me [[-5,-1],[1,3],[4,12],[15,20]]. Since [-5,-3],[-4,-1] and [4,8],[5,10],[10,12] have numbers that intercept each other. I.e., I want the function to return all the "lonely" intervals and the min’s and max’s of the union of the intervals in which their numbers intercept each other.

I have this code that do something similar, but it isn’t what I want yet:

def maxDisjointIntervals(list_): 
      
    # Lambda function to sort the list   
    # elements by second element of pairs  
    list_.sort(key = lambda x: x[1]) 
      
    # First interval will always be  
    # included in set  
    print("[", list_[0][0], ", ", list_[0][1], "]") 
      
    # End point of first interval 
    r1 = list_[0][1] 
      
    for i in range(1, len(list_)): 
        l1 = list_[i][0] 
        r2 = list_[i][1] 
          
        # Check if given interval overlap with  
        # previously included interval, if not  
        # then include this interval and update  
        # the end point of last added interval 
        if l1 > r1: 
            print("[", l1, ", ", r2, "]") 
            r1 = r2 

This code is returning me this output: [[-5, -3],[1, 3],[4, 8],[10, 12],[15, 20]]

repeating what I said before: I want it to return me this output [[-5, -1],[1, 3],[4, 12],[15, 20]]

I hope my explanation didn’t turned out to be too prolix since I’m not a native english speaker. Thanks!

Asked By: Apolo Reis

||

Answers:

So what I understand is that you would like to find intervals which are connected or overlapped, you may do that by using an iterator.

def maxDisjointIntervals(intervals):  # dont use list_ as your variable name
    overlappedIntervals = []

    if len(intervals) == 0:
        return overlappedIntervals

    # sort the intervals using the starting time
    intervals = sorted(intervals, key=lambda interval: interval[0])

    # init the start hour and end hour
    startHour = intervals[0][0]
    endHour = intervals[0][1]

    for interval in intervals[1:]:
        # if there is an overlap
        if interval[0] <= endHour <= interval[1]:
            endHour = interval[1]
        # if there is not an overlap
        else:
            overlappedIntervals.append([startHour, endHour])
            startHour = interval[0]
            endHour = interval[1]
    overlappedIntervals.append([startHour, endHour])
    return overlappedIntervals


print(maxDisjointIntervals([[-5, -3], [-4, -1], [1, 3], [4, 8], [5, 10], [10, 12], [13, 20]]))

output: [[-5, -1], [1, 3], [4, 12], [13, 20]]

Answered By: kennysliding

You can use reduce from functools to merge the intervals together:

intervals = [[-5,-3],[-4,-1],[1,3],[4,8],[5,10],[10,12], [15,20]]

from functools import reduce
disjoints = [*reduce(lambda a,b: a+[b] if not a or b[0]>a[-1][1] else a[:-1]+[[a[-1][0],b[1]]],intervals,[])]

print(disjoints) # [[-5, -1], [1, 3], [4, 12], [15, 20]]

or do the same thing in a basic loop:

disjoints = intervals[:1]
for s,e in intervals[1:]:
    if s>disjoints[-1][-1]: disjoints.append([s,e])
    else: disjoints[-1][-1] = e
    
print(disjoints) # [[-5, -1], [1, 3], [4, 12], [15, 20]]

note: this assumes inclusive ranges. If the end is exclusive use >= instead of >.

Answered By: Alain T.

I think the code should be updated to:

for s,e in intervals[1:]:
    if s > disjoints[-1][-1]: 
        disjoints.append([s,e])
    elif e > disjoints[-1][-1]: 
        disjoints[-1][-1] = e

otherwise it will fail for this case:

intervals = [[-5,-3],[-4,-1],[1,3],**[4,8],[5,7]**,[10,12], [15,20]]

The answer was:

[[-5, -1], [1, 3], [4, **7], [10**, 12], [15, 20]]

instead of:

[[-5, -1], [1, 3], [4, **8], [10**, 12], [15, 20]]
Answered By: macr
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.