Respostas:
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}
Observe que todas as suas chaves têm valores. É que alguns desses valores são strings vazias. Não existe uma chave em um dicionário sem um valor; se não tivesse valor, não estaria no dict.
.items().
{k: v for k, v in metadata.items() if v is not None}
Pode ser ainda mais curto do que a solução do BrenBarn (e mais legível, eu acho)
{k: v for k, v in metadata.items() if v}
Testado com Python 2.7.3.
... if v!=Noneforma: {k: v for k, v in metadata.items() if v!=None}
Se você realmente precisa modificar o dicionário original:
empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]
Observe que temos que fazer uma lista das chaves vazias porque não podemos modificar um dicionário durante a iteração por ele (como você deve ter notado). Isso é menos caro (em termos de memória) do que criar um dicionário totalmente novo, a menos que haja muitas entradas com valores vazios.
.iteritems()por .items(), o primeiro não funciona mais nas versões mais recentes do Python.
A solução de BrenBarn é ideal (e pítônica, devo acrescentar). Aqui está outra solução (fp), no entanto:
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
Se você deseja uma abordagem completa, mas sucinta, para lidar com estruturas de dados do mundo real que geralmente estão aninhadas e podem até conter ciclos, recomendo olhar o utilitário de remapeamento do pacote de utilitários boltons .
Depois de pip install boltonscopiar iterutils.py em seu projeto, basta fazer:
from boltons.iterutils import remap
drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)
Esta página tem muitos outros exemplos, incluindo aqueles que trabalham com objetos muito maiores da API do Github.
É puro Python, por isso funciona em qualquer lugar e é totalmente testado em Python 2.7 e 3.3+. O melhor de tudo é que eu o escrevi exatamente para casos como esse, então se você encontrar um caso que ele não resolve, você pode me bugar para consertá-lo aqui .
Com base na solução de Ryan , se você também tiver listas e dicionários aninhados:
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": "" }] }
Se você tiver um dicionário aninhado e quiser que funcione mesmo para subelementos vazios, pode usar uma variante recursiva da sugestão 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()vez 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)
Com base nas respostas de patriciasz e nneonneo e levando em consideração a possibilidade de que você queira excluir chaves que têm apenas certas coisas falsas (por exemplo ''), mas não outras (por exemplo 0), ou talvez você até queira incluir algumas coisas verdadeiras (por exemplo 'SPAM') , então você poderia fazer uma lista de ocorrências altamente específica:
unwanted = ['', u'', None, False, [], 'SPAM']
Infelizmente, isso não funciona muito bem, porque, por exemplo, 0 in unwantedavalia para True. Precisamos discriminar entre 0outras coisas falsas, então temos que usar is:
any([0 is i for i in unwanted])
... avalia para False.
Agora use-o para delas coisas indesejadas:
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 você quiser um novo dicionário, em vez de modificar metadatano local:
newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
[]
Eu li todas as respostas neste tópico e algumas também se referiram a este tópico: Remover dicts vazios no dicionário aninhado com função recursiva
Eu usei originalmente a solução aqui e funcionou muito bem:
Tentativa 1: Muito quente (sem desempenho ou à prova de 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
Mas algumas questões de desempenho e compatibilidade foram levantadas no mundo Python 2.7:
isinstancevez detypeforloop para eficiênciaitemsvez deiteritemsTentativa 2: Muito frio (falta memorização) :
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! Isso não é recursivo e nem um pouco memoizant.
Tentativa 3: na medida certa (até agora) :
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 limpa a lista usando a scrub_dict(d)implementação 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
Uma maneira alternativa de fazer isso é usar a compreensão do dicionário. Isso deve ser compatível com2.7+
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
Aqui está uma opção se você estiver 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_)
Alguns dos métodos mencionados acima ignoram se houver números inteiros e flutuam com valores 0 e 0,0
Se alguém quiser evitar o acima, pode usar o código abaixo (remove strings vazias e valores None do dicionário aninhado e da lista aninhada):
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 também escrevo atualmente um aplicativo de desktop para meu trabalho com Python, encontrei um aplicativo de entrada de dados quando há muitas entradas e algumas não são obrigatórias, portanto o usuário pode deixá-lo em branco, para fins de validação, é fácil de pegar todas as entradas e, em seguida, descartar a chave ou valor vazio de um dicionário. Portanto, meu código acima mostra como podemos removê-los facilmente, usando a compreensão do dicionário e manter o elemento de valor do dicionário que não está em branco. Eu 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
então loop e delete são os mais rápidos em 160ns, a compreensão da lista é metade mais lenta em ~ 375ns e com uma chamada para dict()é a metade mais lenta novamente em ~ 680ns.
Envolver 3 em uma função o traz de volta para cerca de 275 ns. Além disso, para mim, o PyPy era duas vezes mais rápido do que o neet python.
list(dic.items())py 3. Compreensão de dict, então? del ainda parece mais rápido para uma proporção baixa de valores nulos / vazios. Eu acho que construir essa lista é tão ruim para o consumo de memória do que apenas recriar o dicionário.