This represents ranges by lists of two-element lists. Two-element lists are used as this allows ranges boundaries to be mutated.
# -*- coding: utf-8 -*-
"""
https://stackoverflow.com/questions/64466231/pythonic-way-to-operate-comma-separated-list-of-ranges-1-5-10-25-27-30
Created on Wed Oct 21 16:29:39 2020
@author: Paddy3118
"""
def to_ranges(txt):
return [[int(x) for x in r.strip().split('-')]
for r in txt.strip().split(',')]
def remove_int(rem, ranges):
for i, r in enumerate(ranges):
if r[0] <= rem <= r[1]:
if r[0] == rem: # range min
if r[1] > rem:
r[0] += 1
else:
del ranges[i]
elif r[1] == rem: # range max
if r[0] < rem:
r[1] -= 1
else:
del ranges[i]
else: # inside, range extremes.
r[1], splitrange = rem - 1, [rem + 1, r[1]]
ranges.insert(i + 1, splitrange)
break
if r[0] > rem: # Not in sorted list
break
return ranges
def add_int(add, ranges):
for i, r in enumerate(ranges):
if r[0] <= add <= r[1]: # already included
break
elif r[0] - 1 == add: # rough extend to here
r[0] = add
break
elif r[1] + 1 == add: # rough extend to here
r[1] = add
break
elif r[0] > add: # rough insert here
ranges.insert(i, [add, add])
break
else:
ranges.append([add, add])
return ranges
return consolidate(ranges)
def consolidate(ranges):
"Combine overlapping ranges"
for this, that in zip(ranges, ranges[1:]):
if this[1] + 1 >= that[0]: # Ranges interract
if this[1] >= that[1]: # this covers that
this[:], that[:] = [], this
else: # that extends this
this[:], that[:] = [], [this[0], that[1]]
ranges[:] = [r for r in ranges if r]
return ranges
sent = "1-5,10-25,27-30"
ll = to_ranges(sent)
assert ll == sorted(ll)
Sample calculations
In [68]: ll
Out[68]: [[1, 5], [10, 25], [27, 30]]
In [69]: add_int(26, ll)
Out[69]: [[1, 5], [10, 30]]
In [70]: add_int(9, ll)
Out[70]: [[1, 5], [9, 30]]
In [71]: add_int(7, ll)
Out[71]: [[1, 5], [7, 7], [9, 30]]
In [72]: remove_int(26, ll)
Out[72]: [[1, 5], [7, 7], [9, 25], [27, 30]]
In [73]: remove_int(9, ll)
Out[73]: [[1, 5], [7, 7], [10, 25], [27, 30]]
In [74]: remove_int(7, ll)
Out[74]: [[1, 5], [10, 25], [27, 30]]
In [75]: