mirror of
https://github.com/jlu5/PyLink.git
synced 2024-11-01 01:09:22 +01:00
utils: add remove_range()
""" Removes a range string of (one-indexed) items from the list. Range strings are indices or ranges of them joined together with a ",": e.g. "5", "2", "2-10", "1,3,5-8" See test/test_utils.py for more complete examples. """
This commit is contained in:
parent
f8e3cfa346
commit
c919c523dc
@ -41,5 +41,94 @@ class UtilsTestCase(unittest.TestCase):
|
||||
"\x0305t\x030,1h\x0307,02e\x0308,06 \x0309,13q\x0303,15u\x0311,14i\x0310,05c\x0312,04k\x0302,07 \x0306,08b\x0313,09r\x0305,10o\x0304,12w\x0307,02n\x0308,06 \x0309,13f\x0303,15o\x0311,14x\x0310,05 \x0312,04j\x0302,07u\x0306,08m\x0313,09p\x0305,10s\x0304,12 \x0307,02o\x0308,06v\x0309,13e\x0303,15r\x0311,14 \x0310,05t\x0312,04h\x0302,07e\x0306,08 \x0313,09l\x0305,10a\x0304,12z\x0307,02y\x0308,06 \x0309,13d\x0303,15o\x0311,14g\x0f"),
|
||||
"the quick brown fox jumps over the lazy dog")
|
||||
|
||||
def test_remove_range(self):
|
||||
self.assertEqual(utils.remove_range(
|
||||
"1", [1,2,3,4,5,6,7,8,9]),
|
||||
[2,3,4,5,6,7,8,9])
|
||||
|
||||
self.assertEqual(utils.remove_range(
|
||||
"2,4", [1,2,3,4,5,6,7,8,9]),
|
||||
[1,3,5,6,7,8,9])
|
||||
|
||||
self.assertEqual(utils.remove_range(
|
||||
"1-4", [1,2,3,4,5,6,7,8,9]),
|
||||
[5,6,7,8,9])
|
||||
|
||||
self.assertEqual(utils.remove_range(
|
||||
"1-3,7", [1,2,3,4,5,6,7,8,9]),
|
||||
[4,5,6,8,9])
|
||||
|
||||
self.assertEqual(utils.remove_range(
|
||||
"1-3,5-9", [1,2,3,4,5,6,7,8,9]),
|
||||
[4])
|
||||
|
||||
self.assertEqual(utils.remove_range(
|
||||
"1-2,3-5,6-9", [1,2,3,4,5,6,7,8,9]),
|
||||
[])
|
||||
|
||||
# Anti-patterns, but should be legal
|
||||
self.assertEqual(utils.remove_range(
|
||||
"4,2", [1,2,3,4,5,6,7,8,9]),
|
||||
[1,3,5,6,7,8,9])
|
||||
self.assertEqual(utils.remove_range(
|
||||
"4,4,4", [1,2,3,4,5,6,7,8,9]),
|
||||
[1,2,3,5,6,7,8,9])
|
||||
|
||||
# Empty subranges should be filtered away
|
||||
self.assertEqual(utils.remove_range(
|
||||
",2,,4,", [1,2,3,4,5,6,7,8,9]),
|
||||
[1,3,5,6,7,8,9])
|
||||
|
||||
# Not enough items
|
||||
with self.assertRaises(IndexError):
|
||||
utils.remove_range(
|
||||
"5", ["abcd", "efgh"])
|
||||
with self.assertRaises(IndexError):
|
||||
utils.remove_range(
|
||||
"1-5", ["xyz", "cake"])
|
||||
|
||||
# Ranges going in reverse or invalid
|
||||
with self.assertRaises(ValueError):
|
||||
utils.remove_range(
|
||||
"5-2", [":)", ":D", "^_^"])
|
||||
utils.remove_range(
|
||||
"2-2", [":)", ":D", "^_^"])
|
||||
|
||||
# 0th element
|
||||
with self.assertRaises(ValueError):
|
||||
utils.remove_range(
|
||||
"5,0", list(range(50)))
|
||||
|
||||
# List can't contain None
|
||||
with self.assertRaises(ValueError):
|
||||
utils.remove_range(
|
||||
"1-2", [None, "", 0, False])
|
||||
|
||||
# Malformed indices
|
||||
with self.assertRaises(ValueError):
|
||||
utils.remove_range(
|
||||
" ", ["some", "clever", "string"])
|
||||
utils.remove_range(
|
||||
" ,,, ", ["some", "clever", "string"])
|
||||
utils.remove_range(
|
||||
"a,b,c,1,2,3", ["some", "clever", "string"])
|
||||
|
||||
# Malformed ranges
|
||||
with self.assertRaises(ValueError):
|
||||
utils.remove_range(
|
||||
"1,2-", [":)", ":D", "^_^"])
|
||||
utils.remove_range(
|
||||
"-", [":)", ":D", "^_^"])
|
||||
utils.remove_range(
|
||||
"1-2-3", [":)", ":D", "^_^"])
|
||||
utils.remove_range(
|
||||
"-1-2", [":)", ":D", "^_^"])
|
||||
utils.remove_range(
|
||||
"3--", [":)", ":D", "^_^"])
|
||||
utils.remove_range(
|
||||
"--5", [":)", ":D", "^_^"])
|
||||
utils.remove_range(
|
||||
"-3--5", ["we", "love", "emotes"])
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
51
utils.py
51
utils.py
@ -735,3 +735,54 @@ def strip_irc_formatting(text):
|
||||
for char in _irc_formatting_chars:
|
||||
text = text.replace(char, '')
|
||||
return text
|
||||
|
||||
_subrange_re = re.compile(r'(?P<start>(\d+))-(?P<end>(\d+))')
|
||||
def remove_range(rangestr, mylist):
|
||||
"""
|
||||
Removes a range string of (one-indexed) items from the list.
|
||||
Range strings are indices or ranges of them joined together with a ",":
|
||||
e.g. "5", "2", "2-10", "1,3,5-8"
|
||||
|
||||
See test/test_utils.py for more complete examples.
|
||||
"""
|
||||
if None in mylist:
|
||||
raise ValueError("mylist must not contain None!")
|
||||
|
||||
# Split and filter out empty subranges
|
||||
ranges = filter(None, rangestr.split(','))
|
||||
if not ranges:
|
||||
raise ValueError("Invalid range string %r" % rangestr)
|
||||
|
||||
for subrange in ranges:
|
||||
match = _subrange_re.match(subrange)
|
||||
if match:
|
||||
start = int(match.group('start'))
|
||||
end = int(match.group('end'))
|
||||
|
||||
if end <= start:
|
||||
raise ValueError("Range start (%d) is <= end (%d) in range string %r" %
|
||||
(start, end, rangestr))
|
||||
elif 0 in (end, start):
|
||||
raise ValueError("Got range index 0 in range string %r, this function is one-indexed" %
|
||||
rangestr)
|
||||
|
||||
# For our purposes, make sure the start and end are within the list
|
||||
mylist[start-1], mylist[end-1]
|
||||
|
||||
# Replace the entire range with None's
|
||||
log.debug('utils.remove_range: removing items from %s to %s: %s', start, end, mylist[start-1:end])
|
||||
mylist[start-1:end] = [None] * (end-(start-1))
|
||||
|
||||
elif subrange in string.digits:
|
||||
index = int(subrange)
|
||||
if index == 0:
|
||||
raise ValueError("Got index 0 in range string %r, this function is one-indexed" %
|
||||
rangestr)
|
||||
log.debug('utils.remove_range: removing item %s: %s', index, mylist[index-1])
|
||||
mylist[index-1] = None
|
||||
|
||||
else:
|
||||
raise ValueError("Got invalid subrange %r in range string %r" %
|
||||
(subrange, rangestr))
|
||||
|
||||
return list(filter(lambda x: x is not None, mylist))
|
||||
|
Loading…
Reference in New Issue
Block a user