任意の条件関数に従って辞書をフィルタリングする方法は?


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)機能があると期待していました...


回答:


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のより一般的なアプローチよりも2倍以上高速です。ビューも使用できることに注意してください(iteitemのように、それらはdictアイテムのコピーではありません):{k:vはk、vはpoints.viewitems()if v [0] <5 and v [1] < 5}
dorvak 2013

5
そして、関数呼び出しdict()がコンストラクタ/リテラル​​構文{} doughellmann.com/2012/11/…
dorvak

1
覚えておいてiteritemsはPython 3で削除されました。しかし、あなたは使用することができますitems代わりに。iteritems古いバージョンで動作するように動作します。
エリアスザマリア2016

1
@Datanovice 1つはできると確信しています。より有用な回答を得るのに十分な詳細を備えた新しい質問を開くこともできます;)
Thomas

1
限られた回答で質問を開いたので、理解を深めるためにできるだけ多くの質問を読むことにしました。一つのこぎりより知識がある1ので、ものの脳を選ぶために続け;)私Q: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)各ポイントが常に2Dのみであることがわかっている場合は、やりすぎかもしれません(その場合、同じ制約をで表現する可能性がありますand)が正常に機能します;-)。


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

1
Python 2では、items()の代わりにiteritems()を使用してください
Regisz

2
Python 3.5では、これはエラーを返します。points_small = dict(filter(lambda(a、(b、c)):b <5 and c <5、points.items()))^ SyntaxError:invalid syntax `
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であることを言及する価値があります(PEP 3113を参照)
CiprianTomoiagăJun

タプルを辞書式に比較しますが、これはOPに必要なことではありません。あなたの場合、ポイント(3, 10)はテストに合格します:(3, 10) < (5, 5)はTrueですが、それは間違っています(y5より小さい必要もあります)。
dmitry_romanov


7

Alex Martelliの答えは間違いなくこれを行う最もエレガントな方法だと思いますdictionary.filter(f)が、Pythonのような方法で超すばらしいメソッドに対するあなたの欲求を満たす方法を追加したかっただけです。

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ありがとう、エレガントなコード。私はそれが標準辞書の一部であるべきだと本当に思っています。
Adam Matan 2013年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.