如何根据任意条件函数过滤字典?


212

我有一个要点词典,说:

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

我想创建一个新字典,其中所有x和y值均小于5的点,即点“ a”,“ b”和“ d”。

根据这本书,每个字典都有该items()函数,该函数返回一个(key, pair) 元组列表:

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

所以我写了这个:

>>> 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)}

有没有更优雅的方式?我期待Python具有一些超棒的dictionary.filter(f)功能...


Answers:


427

如今,在Python 2.7及更高版本中,您可以使用dict理解:

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

在Python 3中:

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

15
赞!这比Martellis更通用的方法快两倍以上。请注意,您也可以使用视图(例如项目,它们不是字典项的副本):{k:v表示k,v表示于points.viewitems()中,如果v [0] <5和v [1] < 5}
dorvak

5
这里是一个很好的解释了为什么函数调用的dict()比构造函数/文本语法{}慢doughellmann.com/2012/11/...
dorvak

1
请记住,iteritems在Python 3中已将其删除。但是您可以items改用。它表现出iteritems在较旧版本中的工作方式。
Elias Zamaria '16

1
@Datanovice我确定一个可以。一个人也可以提出一个足够详细的新问题,以获得更有用的答案;)
托马斯(Thomas

1
一个人开了一个回答有限的问题,因此人们诉诸于阅读尽可能多的问题以获得更好的理解。一个人看到了一个知识渊博的人,因此继续动脑筋;)我的问:stackoverflow.com/questions/50104127/…–
Datanovice,

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

如果您使用的是Python 2,并且您可能有很多条目.iteritems().items()则可以选择调用而不是。points

all(x < 5 for x in v)如果您确定每个点始终都是二维的,则可能会过大(在这种情况下,您可能会用表示相同的约束and),但效果很好;-)。


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

1
在Python 2中,使用iteritems()代替items()
Regisz

2
在python 3.5中,这将返回错误:points_small = dict(filter(lambda(a,(b,c)):b <5 and c <5,points.items()))^ SyntaxError:无效的语法`
Mevin Babu

我认为python 3不支持它
-Matanster

15
>>> 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)}

3
太好了!值得一提的是,这是Py3,因为lambda不再能够解开元组参数(请参阅PEP 3113
CiprianTomoiagă18年

您按字典顺序比较元组,这不是OP所需要的。在您的情况下,point (3, 10)将通过测试:(3, 10) < (5, 5)是True,但这是错误的(y也应小于5)。
dmitry_romanov


7

我认为Alex Martelli的答案绝对是做到这一点的最优雅的方法,但只是想添加一种dictionary.filter(f)方法,以Pythonic的方式满足您对超棒方法的需求:

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)

基本上,我们创建一个继承自的类dict,但添加了filter方法。我们确实需要使用.items()过滤,因为.iteritems()在破坏性迭代时使用会引发异常。


+1谢谢,优雅的代码。我真的认为它应该成为标准字典的一部分。
亚当·马坦

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.