Split a list into two parts based on a filter?
Peter Otten
__peter__ at web.de
Mon Jun 10 20:11:04 EDT 2013
Chris Angelico wrote:
> On Tue, Jun 11, 2013 at 6:34 AM, Roy Smith <roy at panix.com> wrote:
>> new_songs = [s for s in songs if s.is_new()]
>> old_songs = [s for s in songs if not s.is_new()]
>
> Hmm. Would this serve?
>
> old_songs = songs[:]
> new_songs = [songs.remove(s) or s for s in songs if s.is_new()]
>
> Python doesn't, AFAIK, have a "destructive remove and return"
> operation, and del is a statement rather than an expression/operator,
> but maybe this basic idea could be refined into something more useful.
> It guarantees to call is_new only once per song.
>
> The iterator version strikes my fancy. Maybe this isn't of use to you,
> but I'm going to try my hand at making one anyway.
> >>> def iterpartition(pred,it):
> """Partition an iterable based on a predicate.
>
> Returns two iterables, for those with pred False and those
True."""
> falses,trues=[],[]
> it=iter(it)
> def get_false():
> while True:
> if falses: yield falses.pop(0)
> else:
> while True:
> val=next(it)
> if pred(val): trues.append(val)
> else: break
> yield val
> def get_true():
> while True:
> if trues: yield trues.pop(0)
> else:
> while True:
> val=next(it)
> if not pred(val):
falses.append(val)
> else: break
> yield val
> return get_false(),get_true()
An alternative implementation, based on itertools.tee:
import itertools
def partition(items, predicate=bool):
a, b = itertools.tee((predicate(item), item) for item in items)
return ((item for pred, item in a if not pred),
(item for pred, item in b if pred))
if __name__ == "__main__":
false, true = partition(range(10), lambda item: item % 2)
print(list(false))
print(list(true))
def echo_odd(item):
print("checking", item)
return item % 2
false, true = partition(range(10), echo_odd)
print("FALSE", [next(false) for _ in range(3)])
print("TRUE", next(true))
print("FALSE", list(false))
print("TRUE", list(true))
More information about the Python-list
mailing list