Ich habe ein Diktat und möchte alle Schlüssel entfernen, für die es leere Wertzeichenfolgen gibt.
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
Was ist der beste Weg, dies zu tun?
Ich habe ein Diktat und möchte alle Schlüssel entfernen, für die es leere Wertzeichenfolgen gibt.
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
Was ist der beste Weg, dies zu tun?
Antworten:
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 Diktat ohne Wert; Wenn es keinen Wert hätte, wäre es nicht im Diktat.
.items()
.
{k: v for k, v in metadata.items() if v is not None}
Es kann sogar kürzer werden als die Lösung von BrenBarn (und meiner Meinung nach besser lesbar)
{k: v for k, v in metadata.items() if v}
Getestet mit Python 2.7.3.
... if v!=None
wie {k: v for k, v in metadata.items() if v!=None}
Wenn Sie das Originalwö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 wir es durchlaufen (wie Sie vielleicht bemerkt haben). Dies ist jedoch kostengünstiger (speichertechnisch) als das Erstellen eines brandneuen Wörterbuchs, es sei denn, es gibt viele Einträge mit leeren Werten.
.iteritems()
mit .items()
, wird der erste nicht mehr funktioniert in neuesten Python - Versionen.
Die Lösung von BrenBarn ist ideal (und pythonisch, könnte ich hinzufügen). Hier ist jedoch eine andere (fp) Lösung:
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
Wenn Sie einen umfassenden und dennoch prägnanten Ansatz für den Umgang mit realen Datenstrukturen wünschen, die häufig verschachtelt sind und sogar Zyklen enthalten können, empfehlen wir Ihnen, das Dienstprogramm remap aus dem Dienstprogramm von bolons zu betrachten .
Nach pip install boltons
oder Kopieren iterutils.py in Ihr Projekt, nur tun:
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 reines Python, funktioniert also überall und ist in Python 2.7 und 3.3+ vollständig getestet. Das Beste von allem ist, dass ich es für genau solche Fälle geschrieben habe. Wenn Sie also einen Fall finden, der nicht funktioniert, können Sie mich nerven, um ihn hier zu beheben .
Basierend auf Ryans Lösung , wenn 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
d = { "things": [{ "name": "" }] }
Wenn Sie ein verschachteltes Wörterbuch haben und möchten, dass dies auch für leere Unterelemente funktioniert, können Sie eine rekursive Variante von BrenBarns Vorschlag 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
items()
anstelle von iteritems()
für Python 3
### 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)
Aufbauend auf den Antworten von patriciasz und nneonneo , und die Möglichkeit entfallen , dass Sie die Schlüssel löschen möchten , dass nur bestimmte falsy Dinge (zB ''
) , aber nicht andere (zB 0
), oder vielleicht wollen Sie auch einige truthy Dinge enthalten (zB 'SPAM'
) Dann könnten Sie eine hochspezifische Hitliste erstellen:
unwanted = ['', u'', None, False, [], 'SPAM']
Leider funktioniert das nicht ganz, weil zum Beispiel 0 in unwanted
ausgewertet wird True
. Wir müssen zwischen 0
und anderen falschen Dingen unterscheiden, also müssen wir verwenden is
:
any([0 is i for i in unwanted])
... bewertet zu False
.
Verwenden Sie es jetzt für del
die unerwünschten Dinge:
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 möchten, anstatt es zu ändern metadata
:
newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
[]
Ich habe alle Antworten in diesem Thread gelesen und einige haben auch auf diesen Thread verwiesen: Entfernen Sie leere Diktate im verschachtelten Wörterbuch mit rekursiver Funktion
Ich habe hier ursprünglich eine Lösung verwendet und es hat großartig 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
statttype
for
aus Effizienzgründen in einer Schleife abitems
stattdessen Python3 Safeiteritems
Versuch 2: Zu kalt (ohne Auswendiglernen) :
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 überhaupt nicht 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
if isinstance(v, list):
, der die Liste mit der ursprünglichen scrub_dict(d)
Implementierung bereinigt . @staticmethod
def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v, dict):
v = scrub_dict(v)
if isinstance(v, list):
v = scrub_list(v)
if not v in (u'', None, {}):
new_dict[k] = v
return new_dict
@staticmethod
def scrub_list(d):
scrubbed_list = []
for i in d:
if isinstance(i, dict):
i = scrub_dict(i)
scrubbed_list.append(i)
return scrubbed_list
Eine alternative Möglichkeit besteht darin, das Wörterbuchverständnis zu verwenden. Dies sollte kompatibel sein mit2.7+
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
Hier ist eine Option, wenn Sie verwenden pandas
:
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 ignorieren, ob Ganzzahlen vorhanden sind, und schweben mit den Werten 0 und 0,0
Wenn jemand das oben Genannte vermeiden möchte, kann er den folgenden Code verwenden (entfernt leere Zeichenfolgen und keine 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
"Da ich derzeit auch eine Desktop-Anwendung für meine Arbeit mit Python schreibe, habe ich in einer Dateneingabeanwendung festgestellt, dass viele Eingaben vorhanden sind und einige nicht obligatorisch sind, sodass der Benutzer sie leer lassen kann. Zu Validierungszwecken ist sie leicht zu greifen alle Einträge und verwerfen dann den leeren Schlüssel oder Wert eines Wörterbuchs. Mein Code über a zeigt also, wie wir sie einfach herausnehmen können, indem wir das Wörterbuchverständnis verwenden und das Wörterbuchwertelement behalten, das nicht leer ist. Ich verwende Python 3.8.3
data = {'':'', '20':'', '50':'', '100':'1.1', '200':'1.2'}
dic = {key:value for key,value in data.items() if value != ''}
print(dic)
{'100': '1.1', '200': '1.2'}
In [7]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: dic = {k: v for k, v in dic.items() if v is not None}
1000000 loops, best of 7: 375 ns per loop
In [8]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: dic = dict((k, v) for k, v in dic.items() if v is not None)
1000000 loops, best of 7: 681 ns per loop
In [10]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: for k, v in dic.items():
...: if v is None:
...: del dic[k]
...:
10000000 loops, best of 7: 160 ns per loop
So ist Loop and Delete bei 160 ns am schnellsten, das Listenverständnis ist bei ~ 375 ns halb so langsam und bei einem Aufruf von dict()
ist es wieder halb so langsam ~ 680 ns.
Wenn Sie 3 in eine Funktion einwickeln, wird sie wieder auf etwa 275 ns reduziert. Auch für mich war PyPy etwa doppelt so schnell wie Neet Python.
list(dic.items())
Py 3 aufrufen. del scheint für ein niedriges Verhältnis von Null / Leer-Werten immer noch schneller zu sein. Ich denke, das Erstellen dieser Liste ist für den Speicherverbrauch genauso schlecht wie das Neuerstellen des Diktats.