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!=None
forma: {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 boltons
copiar 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 unwanted
avalia para True
. Precisamos discriminar entre 0
outras coisas falsas, então temos que usar is
:
any([0 is i for i in unwanted])
... avalia para False
.
Agora use-o para del
as 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 metadata
no 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:
isinstance
vez detype
for
loop para eficiênciaitems
vez deiteritems
Tentativa 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.