Last active
May 31, 2016 20:42
-
-
Save seansummers/a23276e4c1df990649a6 to your computer and use it in GitHub Desktop.
integer range expansion (see also: http://rosettacode.org/wiki/Range_expansion)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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