3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-12-26 12:43:09 +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:
James Lu 2018-06-09 17:03:40 -07:00
parent f8e3cfa346
commit c919c523dc
2 changed files with 140 additions and 0 deletions

View File

@ -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"), "\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") "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__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -735,3 +735,54 @@ def strip_irc_formatting(text):
for char in _irc_formatting_chars: for char in _irc_formatting_chars:
text = text.replace(char, '') text = text.replace(char, '')
return text 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))