Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatically negate a filter

What's the most idiomatic way of writing a filter with a negation?

Example:

is_even= lambda x : x % 2 == 0
odd_numbers= filter( lambda x: not is_even(x), range(10) )

Of course, you can just use list comprehensions - but then you needn't use filter anyway

In case anyone wonders, I've stumbled upon this while trying to split a list based on a condition

like image 237
loopbackbee Avatar asked Jan 09 '15 18:01

loopbackbee


2 Answers

The itertools module includes both ifilter() and ifilterfalse(), which filter elements where the function returns True and False respectively.

odd_numbers = ifilterfalse(is_even, range(10))

Note that, in Python 2, there is a difference between filter and ifilter: odd_numbers here would be an iterator, while filter() would give a list (see itertools.ifilter Vs. filter Vs. list comprehensions). If you actually want to build a list, your example with not seems fine, assuming you are set on using filter - list comprehensions may be more 'idiomatic' (List filtering: list comprehension vs. lambda + filter).

In Python 3, filter() constructs an iterator, not a list, and itertools.filterfalse() is the complement.

like image 93
Lack Avatar answered Oct 20 '22 01:10

Lack


Splitting based on a predicate is called a partition. I would find it more idiomatic to implement partition as a separate function rather than repeat its internals specifically for odd and even numbers. Python 3's Itertools Recipes has the following implementation:

def partition(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'
    # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

It uses filterfalse (as described by @Lack) and tee defined in that module. So your highest-level code would look like:

odds, evens = partition(is_even, range(10))
like image 28
Karl Bielefeldt Avatar answered Oct 20 '22 03:10

Karl Bielefeldt