Skip to content

Instantly share code, notes, and snippets.

@seansummers
Last active May 31, 2016 20:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seansummers/a23276e4c1df990649a6 to your computer and use it in GitHub Desktop.
Save seansummers/a23276e4c1df990649a6 to your computer and use it in GitHub Desktop.
integer range expansion (see also: http://rosettacode.org/wiki/Range_expansion)
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")
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 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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment