Ich habe ein Diktier und möchte alle Schlüssel entfernen, für die leere Wertzeichenfolgen vorhanden sind.
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
Was ist der beste Weg, dies zu tun?
Python 2.X
dict((k, v) for k, v in metadata.iteritems() if v)
Python 2.7 - 3.X
{k: v for k, v in metadata.items() if v is not None}
Beachten Sie, dass alle Ihre Schlüssel Werte haben. Es ist nur so, dass einige dieser Werte die leere Zeichenfolge sind. Es gibt keinen Schlüssel in einem Diktier ohne Wert. Wenn es keinen Wert hätte, wäre es nicht im Dikt.
Es kann sogar noch kürzer sein als BrenBarns Lösung (und lesbarer, denke ich)
{k: v for k, v in metadata.items() if v}
Mit Python getestet 2.7.3.
Wenn Sie das ursprüngliche Wörterbuch wirklich ändern müssen:
empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]
Beachten Sie, dass wir eine Liste der leeren Schlüssel erstellen müssen, da wir ein Wörterbuch nicht ändern können, während Sie es durchlaufen (wie Sie vielleicht bemerkt haben). Dies ist jedoch weniger kostspielig als das Erstellen eines brandneuen Wörterbuchs, es sei denn, es gibt viele Einträge mit leeren Werten.
Wenn Sie einen umfassenden und dennoch prägnanten Ansatz für den Umgang mit realen Datenstrukturen wünschen, die oft verschachtelt sind und sogar Zyklen enthalten können, empfehle ich Ihnen, das Remap-Dienstprogramm aus dem Dienstprogramm boltons zu betrachten.
Nachdem Sie pip install boltons
oder iterutils.py in Ihr Projekt kopiert haben, tun Sie einfach:
from boltons.iterutils import remap
drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)
Diese Seite enthält viele weitere Beispiele, einschließlich solcher, die mit viel größeren Objekten aus der Github-API arbeiten.
Es ist pure Python, es funktioniert also überall und ist in Python 2.7 und 3.3+ vollständig getestet. Das Beste von allem ist, ich habe es für genau solche Fälle geschrieben. Wenn Sie einen Fall finden, mit dem er nicht zurechtkommt, können Sie mich dazu veranlassen, ihn zu beheben rechts hier .
BrenBarns Lösung ist ideal (und Pythonic kann ich hinzufügen). Hier ist jedoch eine andere (fp) Lösung:
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
Basierend auf Ryans Lösung , falls Sie auch Listen und verschachtelte Wörterbücher haben:
Für Python 2:
def remove_empty_from_dict(d):
if type(d) is dict:
return dict((k, remove_empty_from_dict(v)) for k, v in d.iteritems() if v and remove_empty_from_dict(v))
Elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
else:
return d
Für Python 3:
def remove_empty_from_dict(d):
if type(d) is dict:
return dict((k, remove_empty_from_dict(v)) for k, v in d.items() if v and remove_empty_from_dict(v))
Elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
else:
return d
Wenn Sie ein verschachteltes Wörterbuch haben und dies auch für leere Unterelemente funktionieren soll, können Sie eine rekursive Variante des BrenBarn-Vorschlags verwenden:
def scrub_dict(d):
if type(d) is dict:
return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
else:
return d
Ausgehend von den Antworten aus patriciasz und nneonneo und der Berücksichtigung der Möglichkeit, dass Sie möglicherweise Schlüssel löschen möchten, die nur bestimmte falsche Dinge (z. B. ''
) enthalten, nicht jedoch andere (z. B. 0
) oder Sie Wenn Sie sogar einige wahrheitsgemäße Dinge hinzufügen möchten (z. B. 'SPAM'
), können Sie eine sehr spezifische Trefferliste erstellen:
unwanted = ['', u'', None, False, [], 'SPAM']
Leider funktioniert das nicht ganz, weil zum Beispiel 0 in unwanted
zu True
ausgewertet wird. Wir müssen zwischen 0
und anderen falschen Dingen unterscheiden, also müssen wir is
verwenden:
any([0 is i for i in unwanted])
... wird zu False
ausgewertet.
Jetzt del
die unerwünschten Dinge verwenden:
unwanted_keys = [k for k, v in metadata.items() if any([v is i for i in unwanted])]
for k in unwanted_keys: del metadata[k]
Wenn Sie ein neues Wörterbuch wünschen, anstatt metadata
an Ort und Stelle zu ändern:
newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
### example01 -------------------
mydict = { "alpha":0,
"bravo":"0",
"charlie":"three",
"delta":[],
"echo":False,
"foxy":"False",
"golf":"",
"hotel":" ",
}
newdict = dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(vdata) ])
print newdict
### result01 -------------------
result01 ='''
{'foxy': 'False', 'charlie': 'three', 'bravo': '0'}
'''
### example02 -------------------
mydict = { "alpha":0,
"bravo":"0",
"charlie":"three",
"delta":[],
"echo":False,
"foxy":"False",
"golf":"",
"hotel":" ",
}
newdict = dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(str(vdata).strip()) ])
print newdict
### result02 -------------------
result02 ='''
{'alpha': 0,
'bravo': '0',
'charlie': 'three',
'delta': [],
'echo': False,
'foxy': 'False'
}
'''
Für Python 3
dict((k, v) for k, v in metadata.items() if v)
Eine alternative Möglichkeit, dies zu tun, ist die Verwendung des Wörterbuchverständnisses. Dies sollte mit 2.7+
kompatibel sein.
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
Ich habe alle Antworten in diesem Thread gelesen und einige verwiesen auch auf diesen Thread: Leere Wörter in verschachtelten Wörterbüchern mit rekursiver Funktion entfernen
Ich habe hier ursprünglich eine Lösung verwendet und es hat super funktioniert:
Versuch 1: zu heiß (nicht performant oder zukunftssicher)}:
def scrub_dict(d):
if type(d) is dict:
return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
else:
return d
In der Python 2.7-Welt wurden jedoch einige Bedenken hinsichtlich Leistung und Kompatibilität geäußert:
isinstance
anstelle von type
for
Schleife aus Effizienzgründen abitems
anstelle von iteritems
Versuch 2: zu kalt (keine Memoisierung):
def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v,dict):
v = scrub_dict(v)
if not v in (u'', None, {}):
new_dict[k] = v
return new_dict
DOH! Dies ist nicht rekursiv und in keiner Weise memoizant.
Versuch 3: genau richtig (bisher):
def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v,dict):
v = scrub_dict(v)
if not v in (u'', None, {}):
new_dict[k] = v
return new_dict
Hier ist eine Option, wenn Sie pandas
verwenden:
import pandas as pd
d = dict.fromkeys(['a', 'b', 'c', 'd'])
d['b'] = 'not null'
d['c'] = '' # empty string
print(d)
# convert `dict` to `Series` and replace any blank strings with `None`;
# use the `.dropna()` method and
# then convert back to a `dict`
d_ = pd.Series(d).replace('', None).dropna().to_dict()
print(d_)
Einige der oben genannten Methoden werden ignoriert, wenn Ganzzahlen und Gleitkommazahlen mit den Werten 0 und 0,0 vorhanden sind
Wenn jemand vermeiden möchte, dass der obige Code unten verwendet wird (entfernt leere Zeichenfolgen und None-Werte aus dem verschachtelten Wörterbuch und der verschachtelten Liste):
def remove_empty_from_dict(d):
if type(d) is dict:
_temp = {}
for k,v in d.items():
if v == None or v == "":
pass
Elif type(v) is int or type(v) is float:
_temp[k] = remove_empty_from_dict(v)
Elif (v or remove_empty_from_dict(v)):
_temp[k] = remove_empty_from_dict(v)
return _temp
Elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if( (str(v).strip() or str(remove_empty_from_dict(v)).strip()) and (v != None or remove_empty_from_dict(v) != None))]
else:
return d