Wie filtere ich ein Wörterbuch nach einer beliebigen Bedingungsfunktion?


212

Ich habe ein Wörterbuch mit Punkten, sagen wir:

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

Ich möchte ein neues Wörterbuch mit allen Punkten erstellen, deren x- und y-Wert kleiner als 5 ist, dh die Punkte 'a', 'b' und 'd'.

Laut dem Buch hat jedes Wörterbuch die items()Funktion, die eine Liste von (key, pair) Tupeln zurückgibt :

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

Also habe ich folgendes geschrieben:

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

Gibt es einen eleganteren Weg? Ich hatte erwartet, dass Python eine super tolle dictionary.filter(f)Funktion haben würde ...


Antworten:


427

Heutzutage können Sie in Python 2.7 und höher ein Diktatverständnis verwenden:

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

Und in Python 3:

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

15
Upvote! Dies ist mehr als zweimal schneller als der allgemeinere Ansatz von Martellis. Beachten Sie, dass Sie auch Ansichten verwenden können (wie Elemente, sie sind KEINE Kopie der diktierten Elemente): {k: v für k, v in points.viewitems (), wenn v [0] <5 und v [1] < 5}
Dorvak

5
Und hier ist eine gute Erklärung, warum der Funktionsaufruf dict () langsamer ist als die Konstruktor / Literal-Syntax {} teigellmann.com/2012/11/…
dorvak

1
Beachten Sie, dass dies iteritemsin Python 3 entfernt wurde. Sie können es jedoch auch verwenden items. Es verhält sich wie iteritemsin älteren Versionen.
Elias Zamaria

1
@Datanovice Ich bin sicher, man könnte. Man könnte auch eine neue Frage mit genügend Details öffnen, um eine nützlichere Antwort zu erhalten;)
Thomas

1
Man hat eine Frage mit begrenzten Antworten geöffnet, daher hat man so viele Fragen wie möglich gelesen, um ein besseres Verständnis zu erlangen. Man sah einen sachkundigeren und suchte sich
Datanovice

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

Sie können wählen, .iteritems()anstatt .items()in Python 2 aufzurufen und pointsmöglicherweise viele Einträge zu haben.

all(x < 5 for x in v)kann übertrieben sein, wenn Sie sicher sind, dass jeder Punkt immer nur 2D ist (in diesem Fall können Sie dieselbe Einschränkung mit einem ausdrücken and), aber es wird gut funktionieren ;-).


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

1
In Python 2 verwenden Sie iteritems () anstelle von items ()
Regisz

2
In Python 3.5 gibt dies einen Fehler zurück: points_small = dict (Filter (Lambda (a, (b, c)): b <5 und c <5, points.items ())) ^ SyntaxError: ungültige Syntax `
Mevin Babu

Ich denke, es wird nicht in 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
großartig ! Erwähnenswert ist, dass dies Py3 ist, da das Lambda das Tupel-Argument nicht mehr auspacken kann (siehe PEP 3113 )
Ciprian Tomoiagă

Sie vergleichen Tupel lexikografisch, was für OP nicht erforderlich ist. In Ihrem Fall besteht point (3, 10)den Test: (3, 10) < (5, 5)ist wahr, aber falsch ( ysollte auch kleiner als 5 sein).
dmitry_romanov

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

7

Ich denke, dass Alex Martellis Antwort definitiv der eleganteste Weg ist, dies zu tun, aber ich wollte nur einen Weg hinzufügen, um Ihren Wunsch nach einer super fantastischen dictionary.filter(f)Methode auf pythonische Weise zu befriedigen :

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)

Grundsätzlich erstellen wir eine Klasse, die von erbt dict, aber die Filtermethode hinzufügt. Wir müssen .items()für die Filterung verwenden, da die Verwendung .iteritems()während der destruktiven Iteration eine Ausnahme auslöst.


+1 Danke, eleganter Code. Ich denke wirklich, dass es ein Teil des Standardwörterbuchs sein sollte.
Adam Matan

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.