mirror of https://github.com/mahmoud/boltons.git
Merge pull request #76 from blebo/page-ranges
Added range2list() and list2range() functions to strutils.py
This commit is contained in:
commit
52cdf43551
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue