Adam Matan
Adam Matan

Reputation: 136499

How to filter a dictionary according to an arbitrary condition function?

I have a dictionary of points, say:

>>> points={'a':(3,4), 'b':(1,2), 'c':(5,5), 'd':(3,3)}

I want to create a new dictionary with all the points whose x and y value is smaller than 5, i.e. points 'a', 'b' and 'd'.

According to the the book, each dictionary has the items() function, which returns a list of (key, pair) tuple:

>>> points.items()
[('a', (3, 4)), ('c', (5, 5)), ('b', (1, 2)), ('d', (3, 3))]

So I have written this:

>>> for item in [i for i in points.items() if i[1][0]<5 and i[1][1]<5]:
...     points_small[item[0]]=item[1]
...
>>> points_small
{'a': (3, 4), 'b': (1, 2), 'd': (3, 3)}

Is there a more elegant way? I was expecting Python to have some super-awesome dictionary.filter(f) function...

Upvotes: 317

Views: 361839

Answers (7)

Thomas
Thomas

Reputation: 182083

You can use a dict comprehension:

{k: v for k, v in points.items() if v[0] < 5 and v[1] < 5}

And in Python 2, starting from 2.7:

{k: v for k, v in points.iteritems() if v[0] < 5 and v[1] < 5}

Upvotes: 582

benguesmia farid
benguesmia farid

Reputation: 251

>>> points = {'a': (3, 4), 'c': (5, 5), 'b': (1, 2), 'd': (3, 3)}
>>> dict(filter(lambda x: (x[1][0], x[1][1]) < (5, 5), points.items()))

{'a': (3, 4), 'b': (1, 2), 'd': (3, 3)}

Upvotes: 25

qwwqwwq
qwwqwwq

Reputation: 7329

I think that Alex Martelli's answer is definitely the most elegant way to do this, but just wanted to add a way to satisfy your want for a super awesome dictionary.filter(f) method in a Pythonic sort of way:

class FilterDict(dict):
    def __init__(self, input_dict):
        for key, value in input_dict.iteritems():
            self[key] = value
    def filter(self, criteria):
        for key, value in self.items():
            if (criteria(value)):
                self.pop(key)

my_dict = FilterDict( {'a':(3,4), 'b':(1,2), 'c':(5,5), 'd':(3,3)} )
my_dict.filter(lambda x: x[0] < 5 and x[1] < 5)

Basically we create a class that inherits from dict, but adds the filter method. We do need to use .items() for the the filtering, since using .iteritems() while destructively iterating will raise exception.

Upvotes: 10

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799490

dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)

Upvotes: 6

Alex Martelli
Alex Martelli

Reputation: 882751

dict((k, v) for k, v in points.items() if all(x < 5 for x in v))

You could choose to call .iteritems() instead of .items() if you're in Python 2 and points may have a lot of entries.

all(x < 5 for x in v) may be overkill if you know for sure each point will always be 2D only (in that case you might express the same constraint with an and) but it will work fine;-).

Upvotes: 121

user206545
user206545

Reputation:

points_small = dict(filter(lambda (a,(b,c)): b<5 and c < 5, points.items()))

Upvotes: 28

nosklo
nosklo

Reputation: 223152

dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)

Upvotes: 10

Related Questions