1

I found numerous similar questions in other programming languages (ruby, C++, JS, etc) but not for Python. Since Python has e.g. itertools I wonder whether we can do the same more elegantly in Python.

Let's say we have a "complete range", [1,100] and then a subset of ranges within/matching the "complete range":

  • [10,50]
  • [90,100]

How can we extract the not covered positions, in this case [1,9], [51,89]?

This is a toy example, in my real dataset I have ranges up to thousands.

CodeNoob
  • 1,988
  • 1
  • 11
  • 33

3 Answers3

6

Here is a neat solution using itertools.chain: I've assumed the input ranges don't overlap. If they do, they need to be simplified first using a union-of-ranges algorithm.

from itertools import chain

def range_gaps(a, b, ranges):
    ranges = sorted(ranges)
    flat = chain((a-1,), chain.from_iterable(ranges), (b+1,))
    return [[x+1, y-1] for x, y in zip(flat, flat) if x+1 < y]

Taking range_gaps(1, 100, [[10, 50], [90, 100]]) as an example:

  • First sort the ranges in case they aren't already in order. If they are guaranteed to be in order, this step is not needed.
  • Then flat is an iterable which will give the sequence 0, 10, 50, 90, 100, 101.
  • Since flat is lazily evaluated and is consumed by iterating over it, zip(flat, flat) gives a sequence of pairs like (0, 10), (50, 90), (100, 101).
  • The ranges required are then like (1, 9), (51, 89) and the case of (100, 101) should give an empty range so it is discarded.
kaya3
  • 47,440
  • 4
  • 68
  • 97
1

Assuming the list contains only integers, and the sub-ranges are in increasing order and not overlapping, You can use below code.

This code will take all sub ranges one by one, and will compare with original complete range and the sub range before it, to find the missing range.

[start,end]=[1,100]
chunks=[[25,31],[7,15],[74,83]]

print([r for r in [[start,chunks[0][0]-1] if start!=chunks[0][0] else []] + [[chunks[i-1][1]+1, chunks[i][0]-1] for i in range(1,len(chunks))]+[[chunks[-1][1]+1,end] if end!=chunks[-1][1] else []] if r])

Input

[1,100]
[[7,15],[25,31],[74,83]]

Output

[[1, 6], [16, 24], [32, 73], [84, 100]]

If increasing order of sub ranges are not guaranteed. you can include below line to sort chunks.

chunks.sort(key=lambda x: x[0])
Liju
  • 2,273
  • 3
  • 6
  • 21
0

This is a generic solution:

def gap(N, ranges):
    ranges=[(min1, max1), (min2, (max2), ......, (minn, maxn)]
    
    original=set(range(N))
           
    for i in ranges:
        original=original-set(range(i[0], i[1]))

    return original
IoaTzimas
  • 10,538
  • 2
  • 13
  • 30