Aviv Cohn
Aviv Cohn

Reputation: 17193

Is there a filter() opposite builtin?

Is there a function in Python that does the opposite of filter? I.e. keeps the items in the iterable that the callback returns False for? Couldn't find anything.

Upvotes: 29

Views: 24722

Answers (5)

GurstTavo
GurstTavo

Reputation: 334

I had the same situation, but I didn't wanted to have duplicated functions for my filter. This is my solution to filter the items of a dictionary and be able to invert the selection.

req_table = {
    "ID_4001" : {"R_STAT":True, "V_STAT":False, "TYPE":"X", "VEL": 1},
    "ID_0051" : {"R_STAT":True, "V_STAT":True,  "TYPE":"X", "VEL": 23},
    "ID_0741" : {"R_STAT":True, "V_STAT":False, "TYPE":"Y", "VEL": 32},
    "ID_0701" : {"R_STAT":True, "V_STAT":False, "TYPE":"X", "VEL": 2353},
}

def count_prop(prop, with_val, rq_table, invert=False):
    return len(list(filter(lambda tc : (tc[1][prop] == with_val)^invert, rq_table.items())))

print("Items of type X: {}".format(count_prop("TYPE", "X", req_table)))
print("Items of type Y: {}".format(count_prop("TYPE", "X", req_table, invert=True)))

The trick is the ^ which acts as a enabled not.

Upvotes: 0

NichtJens
NichtJens

Reputation: 1899

From Ross Bencina's comment to Martijn Pieters's answer:

Your reasoning is not very convincing. The first case can already be written

positive = filter(some_test, values)

therefore what is asked for should be at least as simple as

negative = filter(not(some_test), values)

I would suggest using a simple negating wrapper function:

def _not(func):
    def not_func(*args, **kwargs):
        return not func(*args, **kwargs)
    return not_func

which allows to write the second line as above (with the added underscore or other distinction, since the not operator cannot and probably should not be overwritten):

negative = filter(_not(some_test), values)

Upvotes: 6

Ross Bencina
Ross Bencina

Reputation: 4173

Another option:

from operator import not_
compose = lambda f, g: lambda x: f( g(x) )

...

ys = filter(compose(not_, predicate), values)

You may have a pre-rolled version of compose() available (e.g. in functional or toolz).

Upvotes: 9

El'endia Starman
El'endia Starman

Reputation: 2244

You can do this with itertools.filterfalse or as Martijn suggests, put a not somewhere inside the lambda you use in your filter.

Upvotes: 25

Martijn Pieters
Martijn Pieters

Reputation: 1122252

No, there is no built-in inverse function for filter(), because you could simply invert the test. Just add not:

positive = filter(lambda v: some_test(v), values)
negative = filter(lambda v: not some_test(v), values)

The itertools module does have itertools.ifilterfalse(), which is rather redundant because inverting a boolean test is so simple. The itertools version always operates as a generator.

Upvotes: 42

Related Questions