seansummers
10/20/2015 - 7:46 PM

integer range expansion (see also: http://rosettacode.org/wiki/Range_expansion)

integer range expansion (see also: http://rosettacode.org/wiki/Range_expansion)

from itertools import tee, izip


def range_to_group(lst):
    """generate string integer sets from a list of integers

    >>> tuple(range_to_group([-3,2,5,6,-4]))
    ('-4--3', '2', '5-6')
    """

    a, b = tee(sorted(set(lst)))
    next(b, None)
    i = izip(a, b)
    for x, y in i:
        start = stop = x
        while y - x == 1:
            stop = y
            x, y = next(i, (0, 0))
        yield ("{}" if start == stop else "{}-{}").format(start, stop)


def range_extract(lst):
    """create string integer sets from list

    >>> range_extract([4,5,7,8,-6,-3,-2,3,4,5,9,10,20])
    '-6,-3--2,3-5,7-10'
    >>> range_extract([1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14])
    '1-6,8-12'
    """

    return ",".join(range_to_group(lst))
    
if __name__ == "__main__":
    import doctest
    doctest.testmod()
from itertools import chain


def group_to_range(group):
    """parse single string integer set to list

    Handles arbitrary whitespace, out-of-order
    and negative integers.

    >>> group_to_range("-3--1")
    [-3, -2, -1]
    >>> group_to_range("3-2")
    [2, 3]
    >>> group_to_range(" 3 -  - 2 ")
    [-2, -1, 0, 1, 2, 3]
    """

    group = "".join(group.split())
    sign, g = ("-", group[1:]) if group.startswith("-") else ("", group)
    r = g.split("-", 1)
    r[0] = "".join((sign, r[0]))
    r = sorted(int(__) for __ in r)
    return range(r[0], 1 + r[-1])


def range_expand(txt):
    """parse string of integer sets with intervals to list

    Handles arbitrary whitespace, overlapping ranges, out-of-order ranges,
    and negative integers.

    >>> range_expand("-6,-3--1,3-5,7-11,14,15,17-20")
    [-6, -3, -2, -1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
    >>> range_expand("1-4,6,3-2, 11, 8 - 12,5,14-14")
    [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14]
    """

    ranges = chain.from_iterable(group_to_range(__) for __ in txt.split(","))
    return sorted(set(ranges))

if __name__ == "__main__":
    import doctest
    doctest.testmod()
from pyparsing import Literal, nums, Word, Optional, Combine, delimitedList, Suppress


def parse_integer(s, l, t):
    return int(t[0])


def parse_integer_range(s, l, t):
    x, y = t[0], t[-1]
    x, y = min(x, y), max(x, y) + 1
    return range(x, y)


def parse_integer_range_list(s, l, t):
    return sorted(set(t))


sign = Literal('+') | Literal('-')
number = Word(nums)

integer = Combine(Optional(sign) + number, adjacent=False)
integer.setParseAction(parse_integer)

integer_range = integer + Suppress('-') + integer
integer_range.setParseAction(parse_integer_range)

integer_range_list = delimitedList(integer_range | integer, ',')
integer_range_list.setParseAction(parse_integer_range_list)

if __name__ == '__main__':
    print integer_range_list.parseString("1-4,6,3-2, 11, 8 - 12,5,14-14")