Last active
September 20, 2016 23:13
-
-
Save sethrh/e97d5c6b0ebc906cf267dc0ec8866a03 to your computer and use it in GitHub Desktop.
Function to simultaneously partition a list into multiple sub-lists based on used-defined criteria. Kind of like a list comprehension with multiple return values.
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
def multifilter(seq, *tests, multiple=False): | |
"""Filters items in a list into sublists corresponding to the | |
supplied test functions. | |
:returns: A list of lists of items matching conditions, in order. The last | |
item in the return value is the list of items that did not | |
match any condition. | |
[[items matching condition0], | |
[items matching condition1], | |
... | |
[items matching condition(N-1)], | |
[non-matching items]] | |
For example, given a list of numbers, s, you can generate a list of even | |
numbers and odd numbers by the following: | |
>>> multifilter(range(10), lambda x: x & 1) | |
[[1, 3, 5, 7, 9], [0, 2, 4, 6, 8]] | |
Also, you can do divisible by 2s and 3s like so: | |
>>> multifilter(range(10), lambda x: divmod(x, 2)[1] == 0, | |
... lambda x: divmod(x, 3)[1] == 0) | |
[[0, 2, 4, 6, 8], [3, 9], [1, 5, 7]] | |
If multiple is True, then each element will be tested against all | |
conditions, resulting in the possibility that the same item will exist in | |
multiple output arrays. In the above example, even though 6 is divisible by | |
3, it's not showing up in the second list. By setting multiple to True, | |
every test applies to each element: | |
>>> multifilter(range(10), lambda x: divmod(x, 2)[1] == 0, | |
... lambda x: divmod(x, 3)[1] == 0, multiple=True) | |
[[0, 2, 4, 6, 8], [0, 3, 6, 9], [1, 5, 7]] | |
""" | |
results = [[] for i in range(len(tests))] | |
nonmatching = [] | |
enumerated_tests = list(enumerate(tests)) # for efficiency | |
for item in seq: | |
found = False | |
for i, cond in enumerated_tests: | |
if cond(item): | |
results[i].append(item) | |
found = True | |
if not multiple: | |
break | |
else: | |
if not found: | |
nonmatching.append(item) | |
results.append(nonmatching) | |
return results | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment