Respuestas:
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}
Tenga en cuenta que todas sus claves tienen valores. Es solo que algunos de esos valores son la cadena vacía. No existe una clave en un dict sin un valor; si no tuviera un valor, no estaría en el dict.
.items()
.
{k: v for k, v in metadata.items() if v is not None}
Puede ser incluso más corto que la solución de BrenBarn (y más legible, creo)
{k: v for k, v in metadata.items() if v}
Probado con Python 2.7.3.
... if v!=None
así: {k: v for k, v in metadata.items() if v!=None}
Si realmente necesita modificar el diccionario original:
empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]
Tenga en cuenta que tenemos que hacer una lista de las claves vacías porque no podemos modificar un diccionario mientras iteramos a través de él (como habrá notado). Sin embargo, esto es menos costoso (en términos de memoria) que crear un diccionario nuevo, a menos que haya muchas entradas con valores vacíos.
.iteritems()
con .items()
, la primera ya no funciona en las versiones más recientes de Python.
La solución de BrenBarn es ideal (y pitónica, debo agregar). Sin embargo, aquí hay otra solución (fp):
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
Si desea un enfoque con todas las funciones, pero conciso, para manejar estructuras de datos del mundo real que a menudo están anidadas e incluso pueden contener ciclos, le recomiendo mirar la utilidad de reasignación del paquete de utilidades de boltons .
Después de pip install boltons
copiar iterutils.py en su proyecto, simplemente haga lo siguiente:
from boltons.iterutils import remap
drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)
Esta página tiene muchos más ejemplos, incluidos los que trabajan con objetos mucho más grandes de la API de Github.
Es Python puro, por lo que funciona en todas partes y está completamente probado en Python 2.7 y 3.3+. Lo mejor de todo es que lo escribí exactamente para casos como este, así que si encuentra un caso que no maneja, puede pedirme que lo solucione aquí mismo .
Basado en la solución de Ryan , si también tiene listas y diccionarios anidados:
Para 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
Para 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": "" }] }
Si tiene un diccionario anidado y desea que esto funcione incluso para subelementos vacíos, puede usar una variante recursiva de la sugerencia de 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()
lugar de iteritems()
para 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'
}
'''
Para python 3
dict((k, v) for k, v in metadata.items() if v)
Basándose en las respuestas de patriciasz y nneonneo , y teniendo en cuenta la posibilidad de que desee eliminar claves que solo tienen ciertas cosas falsas (por ejemplo ''
) pero no otras (por ejemplo 0
), o tal vez incluso desee incluir algunas cosas verdaderas (por ejemplo 'SPAM'
) , entonces podría hacer una lista de resultados muy específica:
unwanted = ['', u'', None, False, [], 'SPAM']
Desafortunadamente, esto no funciona del todo porque, por ejemplo, se 0 in unwanted
evalúa como True
. Necesitamos discriminar entre 0
otras cosas falsas, así que tenemos que usar is
:
any([0 is i for i in unwanted])
... evalúa a False
.
Ahora utilícelo para del
las cosas no deseadas:
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]
Si desea un diccionario nuevo, en lugar de modificarlo metadata
en su lugar:
newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
[]
Leí todas las respuestas en este hilo y algunas se refirieron también a este hilo: Eliminar dictados vacíos en el diccionario anidado con función recursiva
Originalmente usé la solución aquí y funcionó muy bien:
Intento 1: demasiado caliente (sin rendimiento ni preparado para el 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
Pero se plantearon algunas preocupaciones de rendimiento y compatibilidad en el mundo de Python 2.7:
isinstance
lugar detype
for
bucle para mayor eficienciaitems
lugar deiteritems
Intento 2: demasiado frío (carece de memorización) :
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! Esto no es recursivo ni memorable en absoluto.
Intento 3: Justo a la derecha (hasta ahora) :
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):
, que limpia la lista usando la scrub_dict(d)
implementación original . @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
Una forma alternativa de hacerlo es mediante la comprensión del diccionario. Esto debería ser compatible con2.7+
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
Aquí hay una opción si está utilizando 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_)
Algunos de los métodos mencionados anteriormente ignoran si hay números enteros y flotan con valores 0 y 0.0
Si alguien quiere evitar lo anterior, puede usar el siguiente código (elimina las cadenas vacías y los valores Ninguno del diccionario anidado y la lista anidada):
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
"Como actualmente también escribo una aplicación de escritorio para mi trabajo con Python, encontré en la aplicación de entrada de datos cuando hay muchas entradas y algunas no son obligatorias, por lo que el usuario puede dejarla en blanco, para fines de validación, es fácil de tomar todas las entradas y luego descartar la clave vacía o el valor de un diccionario. Entonces, mi código anterior muestra cómo podemos eliminarlos fácilmente, usando la comprensión del diccionario y manteniendo el elemento de valor del diccionario que no está en blanco. Yo 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
por lo que repetir y eliminar es el más rápido a 160 ns, la comprensión de la lista es la mitad de lenta a ~ 375 ns y con una llamada a dict()
es la mitad de lenta de nuevo a ~ 680 ns.
Envolver 3 en una función lo vuelve a bajar a aproximadamente 275ns. También para mí, PyPy fue aproximadamente el doble de rápido que neet python.
list(dic.items())
en py 3. ¿Dict comprensión ftw entonces? del todavía parece más rápido para una relación baja de valores nulos / vacíos. Supongo que construir esa lista es tan malo para el consumo de memoria que simplemente recrear el dict.