5
\$\begingroup\$

Kata: https://www.codewars.com/kata/range-extraction/python

A format for expressing an ordered list of integers is to use a comma separated list of either

  • individual integers
  • or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. The range includes all integers in the interval including both endpoints. It is not considered a range unless it spans at least 3 numbers. For example ("12, 13, 15-17")

Complete the solution so that it takes a list of integers in increasing order and returns a correctly formatted string in the range format.

Example:

solution([-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20])
 # returns "-6,-3-1,3-5,7-11,14,15,17-20"

Courtesy of rosettacode.org

My Code

def solution(lst):
    res = []
    if lst:
        tmp, i, ln = lst[0], 0, len(lst)
        while i < ln:
            tmp, j = lst[i], i
            while j < ln - 1 and lst[j+1] == lst[j]+1:
                j += 1
            if j - i > 1:
                tmp = str(lst[i]) + "-" + str(lst[j])
                i = j+1
            else:
                i = (j if j > i else i+1)
            res.append(tmp)
    return ",".join(str(x) for x in res)
\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

Grouping

If you need to group anything using; groupby is probably the easiest way

Here we can use a itertools.groupby recipe to group the consecutive ranges:

>>> for _, g in groupby(enumerate(lst), lambda i_x : i_x[0] - i_x[1]):
...     print([x for _, x in g])
[-6]
[-3, -2, -1, 0, 1]
[3, 4, 5]
[7, 8, 9, 10, 11]
[14, 15]
[17, 18, 19, 20]

Yielding the correct representation

Now all is left is to check whether there are more then 2 items in the range, and return the range. Else return all the numbers in the range normally

We can make this a generator, which yields either the range or a number, and afterwards join them together.

Full Code

def group_consecutives(lst):
    for _, g in groupby(enumerate(lst), lambda i_x : i_x[0] - i_x[1]):
        r = [x for _, x in g]
        if len(r) > 2:
            yield f"{r[0]}-{r[-1]}"
        else:
            yield from map(str, r)

def range_extraction(lst):
    return ','.join(group_consecutives(lst))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.