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