# Collapse sequences of numbers into ranges

Doable. Let's see if this can be done with pandas.

import pandas as pd

data = ['10215', '10216', '10277', ...]
s = pd.Series(data)
# Find all consecutive rows with a difference of one
# and bin them into groups using cumsum.
v = s.astype(int).diff().bfill().ne(1).cumsum()
# Use groupby and apply to condense the consecutive numbers into ranges.
# This is only done if the group size is >1.
ranges = (
s.groupby(v).apply(
lambda x: '-'.join(x.values[[0, -1]]) if len(x) > 1 else x.item()).tolist())


print (ranges)
['10215-10216',
'10277-10282',
'10292-10293',
'10295-10326',
'10344',
'10399-10406',
'10415-10418',
'10430',
'10448',
'10492-10495',
'10574-10659',
'10707-10710',
'10792-10795',
'10908',
'10936-10939',
'11108-11155',
'11194-11235',
'10101-10102',
'10800',
'11236']


Your data must be sorted for this to work.

You can just use a simple loop here with the following logic:

1. Create a list to store the ranges (ranges).
2. Iterate over the values in your list (l)
3. If ranges is empty, append a list with the first value in l to ranges
4. Otherwise if the difference between the current and previous value is 1, append the current value to the last list in ranges
5. Otherwise append a list with the current value to ranges

Code:

l = ['10215', '10216', '10277', '10278', '10279', '10280', ...]

ranges = []
for x in l:
if not ranges:
ranges.append([x])
elif int(x)-prev_x == 1:
ranges[-1].append(x)
else:
ranges.append([x])
prev_x = int(x)


Now you can compute your final ranges by concatenating the first and last element of each list in ranges (if there are at least 2 elements).

final_ranges = ["-".join([r, r[-1]] if len(r) > 1 else r) for r in ranges]
print(final_ranges)
#['10215-10216',
# '10277-10282',
# '10292-10293',
# '10295-10326',
# '10344',
# '10399-10406',
# '10415-10418',
# '10430',
# '10448',
# '10492-10495',
# '10574-10659',
# '10707-10710',
# '10792-10795',
# '10908',
# '10936-10939',
# '11108-11155',
# '11194-11235',
# '10101-10102',
# '10800',
# '11236']


This also assumes your data is sorted. You could simplify the code to combine items 3 and 5.

For purely educational purposes (this is much more inefficient that the loop above), here's the same thing using map and reduce:

from functools import reduce

def myreducer(ranges, x):
if not ranges:
return [[x]]
elif (int(x) - int(ranges[-1][-1]) == 1):
return ranges[:-1] + [ranges[-1]+[x]]
else:
return ranges + [[x]]

final_ranges = map(
lambda r: "-".join([r, r[-1]] if len(r) > 1 else r),
reduce(myreducer, l, [])
)