Ho un dict e vorrei rimuovere tutte le chiavi per le quali sono presenti stringhe di valori vuote.
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
Qual è il modo migliore per farlo?
Ho un dict e vorrei rimuovere tutte le chiavi per le quali sono presenti stringhe di valori vuote.
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
Qual è il modo migliore per farlo?
Risposte:
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}
Tieni presente che tutte le tue chiavi hanno valori. È solo che alcuni di questi valori sono la stringa vuota. Non esiste una chiave in un dict senza valore; se non avesse un valore, non sarebbe nel dict.
.items()
.
{k: v for k, v in metadata.items() if v is not None}
Può diventare ancora più breve della soluzione di BrenBarn (e più leggibile credo)
{k: v for k, v in metadata.items() if v}
Testato con Python 2.7.3.
... if v!=None
modo: {k: v for k, v in metadata.items() if v!=None}
Se hai davvero bisogno di modificare il dizionario originale:
empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]
Nota che dobbiamo fare un elenco delle chiavi vuote perché non possiamo modificare un dizionario mentre iteriamo attraverso di esso (come potresti aver notato). Tuttavia, è meno costoso (dal punto di vista della memoria) rispetto alla creazione di un dizionario nuovo di zecca, a meno che non ci siano molte voci con valori vuoti.
.iteritems()
con .items()
, il primo non funziona più nelle ultime versioni di Python.
La soluzione di BrenBarn è ideale (e pitonica, potrei aggiungere). Ecco un'altra (fp) soluzione, tuttavia:
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
Se si desidera un approccio completo ma conciso alla gestione delle strutture di dati del mondo reale che sono spesso annidate e possono persino contenere cicli, consiglio di guardare l'utilità di rimappatura dal pacchetto di utilità boltons .
Dopo pip install boltons
o aver copiato iterutils.py nel tuo progetto, fai semplicemente:
from boltons.iterutils import remap
drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)
Questa pagina contiene molti altri esempi, inclusi quelli che lavorano con oggetti molto più grandi dall'API di Github.
È puro Python, quindi funziona ovunque ed è completamente testato in Python 2.7 e 3.3+. Soprattutto, l'ho scritto esattamente per casi come questo, quindi se trovi un caso che non gestisce, puoi infastidirmi per risolverlo proprio qui .
In base alla soluzione di Ryan , se hai anche elenchi e dizionari nidificati:
Per 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
Per 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": "" }] }
Se hai un dizionario annidato e vuoi che funzioni anche per sottoelementi vuoti, puoi usare una variante ricorsiva del suggerimento di BrenBarn:
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()
invece di iteritems()
per 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'
}
'''
Per python 3
dict((k, v) for k, v in metadata.items() if v)
Basandosi sulle risposte di patriciasz e nneonneo , e tenendo conto della possibilità che tu possa voler eliminare chiavi che hanno solo certe cose false (es ''
) ma non altre (es 0
), o forse vuoi anche includere alcune cose vere (es 'SPAM'
) , quindi potresti creare una lista di risultati altamente specifica:
unwanted = ['', u'', None, False, [], 'SPAM']
Sfortunatamente, questo non funziona del tutto, perché ad esempio 0 in unwanted
restituisce True
. Dobbiamo discriminare tra 0
e altre cose false, quindi dobbiamo usare is
:
any([0 is i for i in unwanted])
... restituisce False
.
Ora usalo per del
le cose indesiderate:
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]
Se vuoi un nuovo dizionario, invece di modificarlo metadata
in posizione:
newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
[]
Ho letto tutte le risposte in questo thread e alcune si riferivano anche a questo thread: Rimuovi i dict vuoti nel dizionario annidato con la funzione ricorsiva
Inizialmente ho usato la soluzione qui e ha funzionato benissimo:
Tentativo 1: troppo caldo (non performante o a prova di futuro) :
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
Ma alcuni problemi di prestazioni e compatibilità sono stati sollevati nel mondo di Python 2.7:
isinstance
invece ditype
for
loop per l'efficienzaitems
invece diiteritems
Tentativo 2: troppo freddo (manca la memorizzazione) :
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! Questo non è ricorsivo e per niente memoizant.
Tentativo 3: giusto (finora) :
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):
, che cancella l'elenco utilizzando l' scrub_dict(d)
implementazione originale . @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
Un modo alternativo per farlo è usare la comprensione del dizionario. Questo dovrebbe essere compatibile con2.7+
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
Ecco un'opzione se stai usando 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_)
Alcuni dei metodi sopra menzionati ignorano se sono presenti numeri interi e float con valori 0 e 0,0
Se qualcuno vuole evitare quanto sopra può utilizzare il codice seguente (rimuove le stringhe vuote e i valori Nessuno dal dizionario annidato e dall'elenco annidato):
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
"Dato che attualmente scrivo anche un'applicazione desktop per il mio lavoro con Python, ho trovato nell'applicazione di immissione dati quando ci sono molte voci e alcune non sono obbligatorie, quindi l'utente può lasciarlo vuoto, a scopo di convalida, è facile da afferrare tutte le voci e quindi scartare la chiave o il valore vuoto di un dizionario. Quindi il mio codice sopra mostra come possiamo facilmente eliminarli, usando la comprensione del dizionario e mantenere l'elemento del valore del dizionario che non è vuoto.Uso 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
quindi loop and delete è il più veloce a 160ns, la comprensione dell'elenco è la metà lenta a ~ 375ns e con una chiamata a dict()
è di nuovo la metà lenta ~ 680ns.
Mettere 3 in una funzione lo riporta di nuovo a circa 275 ns. Anche per me PyPy era circa il doppio più veloce di Neet Python.
list(dic.items())
in py 3. Comprensione dei dettami ftw allora? del sembra ancora più veloce per un basso rapporto di valori Null / vuoti. Immagino che costruire quell'elenco sia altrettanto dannoso per il consumo di memoria che ricreare semplicemente il dict.