Filter diktieren, um nur bestimmte Schlüssel zu enthalten?


496

Ich habe eine dict, die eine ganze Reihe von Einträgen hat. Ich interessiere mich nur für einige wenige. Gibt es eine einfache Möglichkeit, alle anderen auszuschneiden?


Es ist hilfreich zu sagen, welche Art von Schlüsseln (Ganzzahlen? Zeichenfolgen? Daten? Beliebige Objekte?) Und ob es einen einfachen Test (Zeichenfolge, Regex, Listenmitgliedschaft oder numerische Ungleichung) gibt, um zu überprüfen, welche Schlüssel ein- oder ausgehen. Oder müssen wir eine beliebige (n) Funktion (en) aufrufen, um dies zu bestimmen.
smci

@smci String-Schlüssel. Denken Sie nicht, dass mir überhaupt in den Sinn gekommen ist, dass ich etwas anderes gebrauchen könnte; Ich habe so lange in JS und PHP
programmiert

Antworten:


656

Ein neues Diktat konstruieren:

dict_you_want = { your_key: old_dict[your_key] for your_key in your_keys }

Verwendet das Wörterbuchverständnis.

Wenn Sie eine Version verwenden, der sie fehlen (z. B. Python 2.6 und früher), machen Sie es dict((your_key, old_dict[your_key]) for ...). Es ist das gleiche, wenn auch hässlicher.

Beachten Sie, dass dies im Gegensatz zur Version von jnnnnn eine stabile Leistung (abhängig nur von der Anzahl Ihrer_Tasten) für old_dicts jeder Größe aufweist. Sowohl in Bezug auf Geschwindigkeit als auch Speicher. Da dies ein Generatorausdruck ist, verarbeitet er jeweils ein Element und durchsucht nicht alle Elemente von old_dict.

Alles an Ort und Stelle entfernen:

unwanted = set(keys) - set(your_dict)
for unwanted_key in unwanted: del your_dict[unwanted_key]

8
"Verwendet Wörterbuchverständnis, wenn Sie eine Version verwenden, der sie fehlen" == version <= 2.6
getekha

8
Löst einen KeyError aus, wenn einer der Filer-Schlüssel in old_dict nicht vorhanden ist. Ich würde {k: d [k] für k im Filter vorschlagen, wenn k in d}
Peter Gibson

1
@PeterGibson Ja, wenn dies Teil der Anforderungen ist, müssen Sie etwas dagegen tun . Ob Sie die Schlüssel stillschweigend ablegen, einen Standardwert hinzufügen oder etwas anderes, hängt davon ab, was Sie tun. Es gibt viele Anwendungsfälle, in denen Ihr Ansatz falsch ist. Es gibt auch viele, bei denen ein fehlender Schlüssel old_dictauf einen Fehler an anderer Stelle hinweist, und in diesem Fall bevorzuge ich einen Fehler gegenüber stillschweigend falschen Ergebnissen.

@delnan, auch der Zusatz "if k in d" verlangsamt dich, wenn d groß ist, ich dachte nur, dass es erwähnenswert ist
Peter Gibson

7
@PeterGibson Nicht, die Wörterbuchsuche ist O (1).

130

Etwas eleganteres Diktatverständnis:

foodict = {k: v for k, v in mydict.items() if k.startswith('foo')}

Upvoted. Ich dachte darüber nach, eine ähnliche Antwort hinzuzufügen. Nur aus Neugier, warum gibt es {k: v für k, v in dict.items () ...} anstatt {k: dict [k] für k in dict ...} Gibt es einen Leistungsunterschied?
Hart Simha

4
Beantwortete meine eigene Frage. Das {k: dict [k] für k in dict ...} ist mindestens 20-25% schneller, zumindest in Python 2.7.6, mit einem Wörterbuch von 26 Elementen (timeit (..., setup = "d =") {chr (x + 97): x + 1 für x im Bereich (26)} ")), abhängig davon, wie viele Elemente herausgefiltert werden (das Herausfiltern von Konsonantenschlüsseln ist schneller als das Herausfiltern von Vokalschlüsseln, weil Sie nachschlagen weniger Artikel). Der Leistungsunterschied kann mit zunehmender Größe Ihres Wörterbuchs möglicherweise an Bedeutung verlieren.
Hart Simha

5
Wäre wahrscheinlich die gleiche Leistung, wenn Sie mydict.iteritems()stattdessen verwenden. .items()erstellt eine andere Liste.
Pat

64

Hier ist ein Beispiel in Python 2.6:

>>> a = {1:1, 2:2, 3:3}
>>> dict((key,value) for key, value in a.iteritems() if key == 1)
{1: 1}

Der Filterteil ist die ifAnweisung.

Diese Methode ist langsamer als die Antwort von Delnan, wenn Sie nur einige von sehr vielen Tasten auswählen möchten.


11
außer ich würde wahrscheinlich verwenden, if key in ('x','y','z')denke ich.
Mpen

Wenn Sie bereits wissen, welche Schlüssel Sie möchten, verwenden Sie die Antwort von delnan. Wenn Sie jeden Schlüssel mit einer if-Anweisung testen müssen, verwenden Sie die Antwort von ransford.
jnnnnn

1
Diese Lösung hat noch einen Vorteil. Wenn das Wörterbuch von einem teuren Funktionsaufruf zurückgegeben wird (dh ein / old_dict ist ein Funktionsaufruf), ruft diese Lösung die Funktion nur einmal auf. In einer imperativen Umgebung ist das Speichern des von der Funktion zurückgegebenen Wörterbuchs in einer Variablen keine große Sache, aber in einer funktionalen Umgebung (z. B. in einem Lambda) ist dies eine wichtige Beobachtung.
gae123


20

Code 1:

dict = { key: key * 10 for key in range(0, 100) }
d1 = {}
for key, value in dict.items():
    if key % 2 == 0:
        d1[key] = value

Code 2:

dict = { key: key * 10 for key in range(0, 100) }
d2 = {key: value for key, value in dict.items() if key % 2 == 0}

Code 3:

dict = { key: key * 10 for key in range(0, 100) }
d3 = { key: dict[key] for key in dict.keys() if key % 2 == 0}

Alle Teile der Codeleistung werden mit timeit unter Verwendung von number = 1000 gemessen und 1000 Mal für jedes Codeteil gesammelt.

Geben Sie hier die Bildbeschreibung ein

Für Python 3.6 ist die Leistung von drei Filterarten fast gleich. Für Python 2.7 ist Code 3 etwas schneller.


nur neugierig, hast du diese Handlung aus Python gemacht?
user5359531

1
ggplot2 in R - Teil von tidyverse
keithpjolley

18

Dieser einzeilige Lambda sollte funktionieren:

dictfilt = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])

Hier ist ein Beispiel:

my_dict = {"a":1,"b":2,"c":3,"d":4}
wanted_keys = ("c","d")

# run it
In [10]: dictfilt(my_dict, wanted_keys)
Out[10]: {'c': 3, 'd': 4}

Es ist ein grundlegendes Listenverständnis, das über Ihre Diktatschlüssel (i in x) iteriert und eine Liste von Tupelpaaren (Schlüssel, Wert) ausgibt, wenn der Schlüssel in Ihrer gewünschten Schlüsselliste (y) vorhanden ist. Ein dict () umschließt das Ganze, um es als dict-Objekt auszugeben.


Sollte ein setfür verwenden wanted_keys, sieht aber sonst gut aus.
Mpen

Dies gibt mir ein leeres Wörterbuch, wenn mein ursprüngliches Wörterbuch Listen anstelle von Werten enthält. Problemumgehungen?
FaCoffee

@Francesco, können Sie ein Beispiel geben? Wenn ich: ausführe dictfilt({'x':['wefwef',52],'y':['iuefiuef','efefij'],'z':['oiejf','iejf']}, ('x','z')), wird es {'x': ['wefwef', 52], 'z': ['oiejf', 'iejf']}wie beabsichtigt zurückgegeben.
Jim

Ich versuchte es mit: dict={'0':[1,3], '1':[0,2,4], '2':[1,4]}und das Ergebnis war {}, was ich als leeres Diktat annahm.
FaCoffee

Eine Sache, "diktieren" ist ein reserviertes Wort, daher sollten Sie es nicht verwenden, um ein Diktat zu benennen. Was waren die Schlüssel, die Sie herausziehen wollten? Wenn ich laufe : foo = {'0':[1,3], '1':[0,2,4], '2':[1,4]}; dictfilt(foo,('0','2')), bekomme ich: {'0': [1, 3], '2': [1, 4]}was das beabsichtigte Ergebnis ist
Jim

14

Angesichts Ihres Originalwörterbuchs origund der Einträge, an denen Sie interessiert sind keys:

filtered = dict(zip(keys, [orig[k] for k in keys]))

Das ist nicht so schön wie Delnans Antwort, sollte aber in jeder interessierenden Python-Version funktionieren. Es ist jedoch für jedes Element keysin Ihrem ursprünglichen Wörterbuch fragil .


Nun, dies ist im Grunde eine eifrige Version der "Tupelgenerator-Version" meines Diktatverständnisses. Sehr kompatibel, obwohl Generatorausdrücke in 2.4, Frühjahr 2005, eingeführt wurden - im Ernst, verwendet dies noch jemand?

1
Ich bin nicht anderer Meinung; 2.3 sollte eigentlich nicht mehr existieren. Als veraltete Umfrage zur Verwendung von 2.3: moinmo.in/PollAboutRequiringPython24 Kurzversion: RHEL4, SLES9, ausgeliefert mit OS X 10.4
Kai

7

Basierend auf der akzeptierten Antwort von Delnan.

Was ist, wenn einer Ihrer gewünschten Schlüssel nicht im old_dict enthalten ist? Die Delnan-Lösung löst eine KeyError-Ausnahme aus, die Sie abfangen können. Wenn Sie das nicht brauchen, möchten Sie vielleicht:

  1. Fügen Sie nur Schlüssel hinzu, die sowohl im old_dict als auch in Ihrem Satz von want_keys vorhanden sind.

    old_dict = {'name':"Foobar", 'baz':42}
    wanted_keys = ['name', 'age']
    new_dict = {k: old_dict[k] for k in set(wanted_keys) & set(old_dict.keys())}
    
    >>> new_dict
    {'name': 'Foobar'}
  2. haben einen Standardwert für Schlüssel, der nicht in old_dict festgelegt ist.

    default = None
    new_dict = {k: old_dict[k] if k in old_dict else default for k in wanted_keys}
    
    >>> new_dict
    {'age': None, 'name': 'Foobar'}

Sie könnten auch tun{k: old_dict.get(k, default) for k in ...}
Moberg

6

Diese Funktion erledigt den Trick:

def include_keys(dictionary, keys):
    """Filters a dict by only including certain keys."""
    key_set = set(keys) & set(dictionary.keys())
    return {key: dictionary[key] for key in key_set}

Genau wie die Version von Delnan verwendet diese Version das Wörterbuchverständnis und bietet eine stabile Leistung für große Wörterbücher (abhängig nur von der Anzahl der zulässigen Schlüssel und nicht von der Gesamtzahl der Schlüssel im Wörterbuch).

Und genau wie die Version von MyGGan ermöglicht diese Version, dass Ihre Liste von Schlüsseln Schlüssel enthält, die möglicherweise nicht im Wörterbuch vorhanden sind.

Und als Bonus ist hier die Umkehrung, wo Sie ein Wörterbuch erstellen können, indem Sie bestimmte Schlüssel im Original ausschließen:

def exclude_keys(dictionary, keys):
    """Filters a dict by excluding certain keys."""
    key_set = set(dictionary.keys()) - set(keys)
    return {key: dictionary[key] for key in key_set}

Beachten Sie, dass der Vorgang im Gegensatz zur Version von delnan nicht an Ort und Stelle ausgeführt wird, sodass die Leistung von der Anzahl der Schlüssel im Wörterbuch abhängt. Dies hat jedoch den Vorteil, dass die Funktion das bereitgestellte Wörterbuch nicht ändert.

Bearbeiten: Es wurde eine separate Funktion zum Ausschließen bestimmter Tasten von einem Diktat hinzugefügt.


Sie sollten es keysdurch jede Art von Iteration zulassen , wie das, was Set akzeptiert.
Mpen

Ah, guter Anruf, danke, dass Sie darauf hingewiesen haben. Ich werde das Update machen.
Ryan

Ich frage mich, ob Sie mit zwei Funktionen besser dran sind. Wenn Sie 10 Personen gefragt haben, "bedeutet invertdies, dass das keysArgument beibehalten wird oder dass das keysArgument zurückgewiesen wird?", Wie viele von ihnen würden zustimmen?
Skatenerd

Aktualisiert. Lass mich wissen was du denkst.
Ryan

Dies scheint nicht zu funktionieren, wenn das Eingabediktat Listen anstelle von Werten enthält. In diesem Fall erhalten Sie ein ungültiges Diktat. Problemumgehungen?
FaCoffee

4

Wenn wir ein neues Wörterbuch mit entfernten ausgewählten Schlüsseln erstellen möchten, können wir das Wörterbuchverständnis verwenden.
Zum Beispiel:

d = {
'a' : 1,
'b' : 2,
'c' : 3
}
x = {key:d[key] for key in d.keys() - {'c', 'e'}} # Python 3
y = {key:d[key] for key in set(d.keys()) - {'c', 'e'}} # Python 2.*
# x is {'a': 1, 'b': 2}
# y is {'a': 1, 'b': 2}

Ordentlich. Funktioniert nur in Python 3. Python 2 sagt "TypeError: Nicht unterstützte Operandentypen für -: 'list' und 'set'"
mpen

Set (d.keys ()) für Python 2 hinzugefügt. Dies funktioniert, wenn ich laufe.
Srivastava

2

Andere Option:

content = dict(k1='foo', k2='nope', k3='bar')
selection = ['k1', 'k3']
filtered = filter(lambda i: i[0] in selection, content.items())

Sie erhalten jedoch einen list(Python 2) oder einen Iterator (Python 3), der von zurückgegeben wird filter(), nicht einen dict.


Wickeln Sie filteredein dictund Sie erhalten das Wörterbuch zurück!
CMCDragonkai

1

Kurzform:

[s.pop(k) for k in list(s.keys()) if k not in keep]

Wie die meisten Antworten vermuten lassen, müssen wir ein doppeltes Objekt erstellen, sei es ein listoder , um die Prägnanz aufrechtzuerhalten dict. Dieser erstellt einen Wegwerfartikel list, löscht jedoch die Schlüssel im Original dict.


0

Hier ist eine weitere einfache Methode, die delin einem Liner verwendet wird:

for key in e_keys: del your_dict[key]

e_keysist die Liste der auszuschließenden Schlüssel. Es wird Ihr Diktat aktualisieren, anstatt Ihnen ein neues zu geben.

Wenn Sie ein neues Ausgabediktat wünschen, erstellen Sie vor dem Löschen eine Kopie des Diktats:

new_dict = your_dict.copy()           #Making copy of dict

for key in e_keys: del new_dict[key]

0

Sie könnten verwenden python-benedict, es ist eine diktierte Unterklasse.

Installation: pip install python-benedict

from benedict import benedict

dict_you_want = benedict(your_dict).subset(keys=['firstname', 'lastname', 'email'])

Es ist Open Source auf GitHub: https://github.com/fabiocaccamo/python-benedict


Haftungsausschluss: Ich bin der Autor dieser Bibliothek.

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.