Merge pull request #76 from blebo/page-ranges

Added range2list() and list2range() functions to strutils.py
This commit is contained in:
Mahmoud Hashemi 2016-05-23 01:13:35 -07:00
commit 52cdf43551
2 changed files with 133 additions and 1 deletions

View File

@ -31,7 +31,7 @@ __all__ = ['camel2under', 'under2camel', 'slugify', 'split_punct_ws',
'asciify', 'is_ascii', 'is_uuid', 'html2text', 'strip_ansi',
'bytes2human', 'find_hashtags', 'a10n', 'gunzip_bytes',
'iter_splitlines', 'indent', 'escape_shell_args',
'args2cmd', 'args2sh']
'args2cmd', 'args2sh', 'parse_int_list', 'format_int_list']
_punct_ws_str = string.punctuation + string.whitespace
@ -845,3 +845,116 @@ def args2cmd(args, sep=' '):
result.append('"')
return ''.join(result)
def parse_int_list(range_string, delim=',', range_delim='-'):
"""Returns a sorted list of integers based on *range_string*. Reverse of :func:`format_int_list`.
Args:
range_string (str): String of comma separated integers or ranges (e.g. '1,2,4-6,8'). Typical of a custom page
range string used in printer dialogs.
delim (char): Defaults to ','. Separates integers and contiguous ranges of integers.
range_delim (char): Defaults to '-'. Indicates a contiguous range of integers.
>>> parse_int_list('1,3,5-8,10-11,15')
[1, 3, 5, 6, 7, 8, 10, 11, 15]
"""
output = []
for x in range_string.strip().split(delim):
# Range
if range_delim in x:
range_limits = list(map(int, x.split(range_delim)))
output += list(range(min(range_limits), max(range_limits)+1))
# Empty String
elif not x:
continue
# Integer
else:
output.append(int(x))
return sorted(output)
def format_int_list(int_list, delim=',', range_delim='-', delim_space=False):
"""Returns a sorted range string from a list of integers (*int_list*). Contiguous ranges of integers are
collapsed to min and max values. Reverse of :func:`parse_int_list`.
Args:
int_list (list): List of integers to be converted into a range string (e.g. [1,2,4,5,6,8]).
delim (char): Defaults to ','. Separates integers and contiguous ranges of integers.
range_delim (char): Defaults to '-'. Indicates a contiguous range of integers.
delim_space (bool): Defaults to ``False``. If ``True``, adds a space after all *delim* characters.
>>> format_int_list([1,3,5,6,7,8,10,11,15])
'1,3,5-8,10-11,15'
"""
output = []
contig_range = collections.deque()
for x in sorted(int_list):
# Handle current (and first) value.
if len(contig_range) < 1:
contig_range.append(x)
# Handle current value, given multiple previous values are contiguous.
elif len(contig_range) > 1:
delta = x - contig_range[-1]
# Current value is contiguous.
if delta == 1:
contig_range.append(x)
# Current value is non-contiguous.
elif delta > 1:
range_substr = '{0:d}{1}{2:d}'.format(min(contig_range), range_delim, max(contig_range))
output.append(range_substr)
contig_range.clear()
contig_range.append(x)
# Current value repeated.
else:
continue
# Handle current value, given that contiguous integers haven't been previously detected.
else:
delta = x - contig_range[0]
# Current value is contiguous.
if delta == 1:
contig_range.append(x)
# Current value is non-contiguous.
elif delta > 1:
output.append('{0:d}'.format(contig_range.popleft()))
contig_range.append(x)
# Current value repeated.
else:
continue
# Handle the last value.
else:
# Last value is non-contiguous.
if len(contig_range) == 1:
output.append('{0:d}'.format(contig_range.popleft()))
contig_range.clear()
# Last value is part of contiguous range.
elif len(contig_range) > 1:
range_substr = '{0:d}{1}{2:d}'.format(min(contig_range), range_delim, max(contig_range))
output.append(range_substr)
contig_range.clear()
if delim_space:
output_str = (delim+' ').join(output)
else:
output_str = delim.join(output)
return output_str

View File

@ -24,3 +24,22 @@ def test_is_uuid():
assert strutils.is_uuid(str(uuid.uuid4())) == True
assert strutils.is_uuid(str(uuid.uuid4()), version=1) == False
assert strutils.is_uuid(set('garbage')) == False
def test_parse_int_list():
assert strutils.parse_int_list("1,3,5-8,10-11,15") == [1, 3, 5, 6, 7, 8, 10, 11, 15]
assert strutils.parse_int_list("1,3,5-8,10-11,15,") == [1, 3, 5, 6, 7, 8, 10, 11, 15]
assert strutils.parse_int_list(",1,3,5-8,10-11,15") == [1, 3, 5, 6, 7, 8, 10, 11, 15]
assert strutils.parse_int_list(" 1, 3 ,5-8,10-11,15 ") == [1, 3, 5, 6, 7, 8, 10, 11, 15]
assert strutils.parse_int_list("3,1,5-8,10-11,15") == [1, 3, 5, 6, 7, 8, 10, 11, 15]
assert strutils.parse_int_list("5-8") == [5, 6, 7, 8]
assert strutils.parse_int_list("8-5") == [5, 6, 7, 8]
def test_format_int_list():
assert strutils.format_int_list([1, 3, 5, 6, 7, 8, 10, 11, 15]) == '1,3,5-8,10-11,15'
assert strutils.format_int_list([5, 6, 7, 8]) == '5-8'
assert strutils.format_int_list([1, 3, 5, 6, 7, 8, 10, 11, 15], delim_space=True) == '1, 3, 5-8, 10-11, 15'
assert strutils.format_int_list([5, 6, 7, 8], delim_space=True) == '5-8'