Andrius
Andrius

Reputation: 21168

Python - dynamic filter for list comprehension?

I want to write a method that would filter given list of objects depending on what operator or maybe function was provided to filter by.

Now my method looks like this:

def filter_by_names(objects, names, exclude=False):
    if exclude:
        return [obj for obj in objects if obj.name not in names]
    else:
        return [obj for obj in objects if obj.name in names]

Now it has two filter options, to return objects if its name is in provided names list or to do opposite: return objects whose name is not in provided names list.

What I want is to be able to dynamically specify how to filter. I though using operator library, but it seems there is no not in operator, so I would need to combine in with not which is a bit clunky.

I was thinking of using lambda like:

def filter_by_names(objects, names, fun=lambda obj, names: obj.name in names):
    return [obj for obj in objects if fun(obj, names)]

This one works, but I was wondering maybe there is some better way to do something like that? With lambda I would always need to specify whole function even if I just only need different operator.

Upvotes: 2

Views: 2253

Answers (4)

Copperfield
Copperfield

Reputation: 8510

how about using the xor operator, for example

>>> n = range(0,11,2)
>>> n
[0, 2, 4, 6, 8, 10]
>>> exclude = True
>>> [ x for x in range(10) if (x in n) ^ exclude ] # only the one that are not in n
[1, 3, 5, 7, 9]
>>> exclude = False
>>> [ x for x in range(10) if (x in n) ^ exclude ] # only the one that are also in n
[0, 2, 4, 6, 8]
>>> 

this work because the xor of equals result in false and the xor of different result in true

Upvotes: 2

jasonharper
jasonharper

Reputation: 9597

def filter_by_names(objects, names, exclude=False):
    return [obj for obj in objects if (obj.name in names) == (not exclude)]

Writing that with not exclude (rather than (obj.name not in names) == exclude)) allows you to pass an arbitrary truthy/falsy value for 'exclude', not just True or False.

Upvotes: 1

Andrius
Andrius

Reputation: 21168

For now I'm going with this approach. It is kind of limited, but it does what I currently need.

def filter_by_names(objects, names, exclude=False):

    def _filter(names, name):
        if exclude:
            return name not in names
        return name in names

    return [obj for obj in objects if _filter(names, obj.name)]

P.S. I dropped operator library, because using function which is limited either way, there is no need to have operator lib.

Upvotes: 0

Prune
Prune

Reputation: 77850

Start with Python operators. contains and not should be enough to give you the desired control. However, this will be barely shorter than your current code, and the result is harder to read. On the other hand, this should give you plenty of lovely ideas on what you want to configure next.

Upvotes: 0

Related Questions