I'm dealing with a list of integers that represent the pages in which a keyword was found. I would like to build block of code that converts this list into a string with a specific format that follow some simple rules. Single integers are converted into string. Consecutive integers are considered as intervals (left bound, hyphen, right bound) and then converted into string. Each conversion is comma separated. This is an example of input and desired output:
input = [4, 5, 6, 7, 8, 9, 10, 22, 23, 26, 62, 63, 113, 137, 138, 139]
desired_output = "4-10, 22, 23, 26, 62, 63, 113, 137-139"
I wrote this code:
res = [4, 5, 6, 7, 8, 9, 10, 22, 23, 26, 62, 63, 113, 137, 138, 139]
if len(res)>0:
resStr = str(res[0])
isConsecutive = False
for index in range(1, len(res)):
diff = res[index] - res[index-1]
if diff == 1:
isConsecutive = True
if index == len(res)-1:
resStr = resStr + "-" + str(res[index])
continue
else:
if isConsecutive:
isConsecutive = False
resStr = resStr + "-" + str(res[index-1]) + ", " + str(res[index])
else:
resStr = resStr + ", " + str(res[index])
print(res)
print(resStr)
This code gives me as a result:
4-10, 22-23, 26, 62-63, 113, 137-139
It doesn't recognize that only two consecutive numbers have not be considered as an interval: "22, 23" and not "22-23", as well as "62, 63" and not "62-63". How can be solved this issue? Is there a simpler or more efficient way to perform the conversion?
Track first and last of a range of consecutive numbers, collect the string of the range when a number is non-consecutive. I used a sub-function to avoid repeated code:
res = [4, 5, 6, 7, 8, 9, 10, 22, 23, 26, 62, 63, 113, 137, 138, 139]
def span(numbers):
if len(numbers) == 0: # empty case
return ''
result = []
def _process(first, last):
if first != last: # range, but handle consecutive case
result.append(f"{first}{'-' if last - first > 1 else ', '}{last}")
else:
result.append(str(first)) # first==last, single number
first = last = numbers[0] # initialize range tracking to first number
for item in numbers[1:]: # iterate remaining numbers
if item <= last: # input not increasing
raise ValueError('not consecutive numbers')
if item == last + 1: # next number is consecutive
last = item
else:
_process(first, last) # next number not consecutive
first = last = item # reset for next range
_process(first, last) # no more numbers, finish last range
return ', '.join(result)
print(res)
print(span(res))
# Test cases
print(span([]))
print(span([1]))
print(span([1,2]))
print(span([1,2,3]))
print(span([1,2,4]))
print(span([1,3]))
print(span([1,3,4]))
print(span([1,3,4,5]))
print(span([1,2,3,5]))
print(span([1,2,4,5,6]))
print(span([1,2,3,5,6]))
print(span([1,2,3,5,6,7]))
Output:
[4, 5, 6, 7, 8, 9, 10, 22, 23, 26, 62, 63, 113, 137, 138, 139]
4-10, 22, 23, 26, 62, 63, 113, 137-139
1
1, 2
1-3
1, 2, 4
1, 3
1, 3, 4
1, 3-5
1-3, 5
1, 2, 4-6
1-3, 5, 6
1-3, 5-7