Réponses:
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}
Notez que toutes vos clés ont des valeurs. C'est juste que certaines de ces valeurs sont la chaîne vide. Il n'y a pas de clé dans un dict sans valeur; s'il n'avait pas de valeur, ce ne serait pas dans le dict.
.items()
.
{k: v for k, v in metadata.items() if v is not None}
Il peut être encore plus court que la solution de BrenBarn (et plus lisible je pense)
{k: v for k, v in metadata.items() if v}
Testé avec Python 2.7.3.
... if v!=None
comme ceci: {k: v for k, v in metadata.items() if v!=None}
Si vous avez vraiment besoin de modifier le dictionnaire d'origine:
empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]
Notez que nous devons faire une liste des clés vides car nous ne pouvons pas modifier un dictionnaire en l'itérant (comme vous l'avez peut-être remarqué). Cela coûte moins cher (en termes de mémoire) que de créer un tout nouveau dictionnaire, à moins qu'il n'y ait beaucoup d'entrées avec des valeurs vides.
.iteritems()
par .items()
, le premier ne fonctionne plus dans les dernières versions de Python.
La solution de BrenBarn est idéale (et pythonique, pourrais-je ajouter). Voici cependant une autre solution (fp):
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
Si vous souhaitez une approche complète, mais succincte, de la gestion des structures de données du monde réel qui sont souvent imbriquées et peuvent même contenir des cycles, je vous recommande de consulter l'utilitaire de remappage du package d'utilitaires boltons .
Après pip install boltons
ou en copiant iterutils.py dans votre projet, faites simplement:
from boltons.iterutils import remap
drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)
Cette page contient de nombreux autres exemples, y compris ceux fonctionnant avec des objets beaucoup plus volumineux de l'API de Github.
C'est pur-Python, donc cela fonctionne partout, et est entièrement testé en Python 2.7 et 3.3+. Mieux encore, je l'ai écrit pour exactement des cas comme celui-ci, donc si vous trouvez un cas qu'il ne gère pas, vous pouvez me déranger pour le réparer ici .
Basé sur la solution de Ryan , si vous avez également des listes et des dictionnaires imbriqués:
Pour 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
Pour 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 vous avez un dictionnaire imbriqué et que vous voulez que cela fonctionne même pour des sous-éléments vides, vous pouvez utiliser une variante récursive de la suggestion 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()
place de iteritems()
pour 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'
}
'''
Pour python 3
dict((k, v) for k, v in metadata.items() if v)
En vous basant sur les réponses de patriciasz et nneonneo , et en tenant compte de la possibilité que vous souhaitiez supprimer des clés qui n'ont que certaines fausses choses (par exemple ''
) mais pas d'autres (par exemple 0
), ou peut-être que vous voulez même inclure des choses véridiques (par exemple 'SPAM'
) , vous pouvez alors créer une liste de résultats très spécifique:
unwanted = ['', u'', None, False, [], 'SPAM']
Malheureusement, cela ne fonctionne pas tout à fait, car par exemple 0 in unwanted
évalue à True
. Nous devons faire la distinction entre les 0
choses fausses et d'autres, nous devons donc utiliser is
:
any([0 is i for i in unwanted])
... évalue à False
.
Maintenant, utilisez-le pour del
les choses indésirables:
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 vous voulez un nouveau dictionnaire, au lieu de le modifier metadata
sur place:
newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
[]
J'ai lu toutes les réponses dans ce fil et certaines se sont également référées à ce fil: Supprimez les dicts vides dans le dictionnaire imbriqué avec fonction récursive
J'ai initialement utilisé la solution ici et cela a très bien fonctionné:
Tentative 1: Trop chaud (pas performant ni à l'épreuve du temps) :
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
Mais certains problèmes de performances et de compatibilité ont été soulevés dans le monde Python 2.7:
isinstance
au lieu detype
for
boucle pour plus d'efficacitéitems
au lieu deiteritems
Tentative 2: Trop froid (manque de mémorisation) :
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! Ce n'est pas récursif et pas du tout mémoizant.
Tentative 3: juste bien (jusqu'à présent) :
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):
, qui nettoie la liste en utilisant l' scrub_dict(d)
implémentation d' origine . @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
Une autre façon de procéder consiste à utiliser la compréhension du dictionnaire. Cela devrait être compatible avec2.7+
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
Voici une option si vous utilisez 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_)
Certaines des méthodes mentionnées ci-dessus ignorent s'il y a des entiers et des flottants avec des valeurs 0 et 0,0
Si quelqu'un veut éviter ce qui précède, vous pouvez utiliser le code ci-dessous (supprime les chaînes vides et les valeurs None du dictionnaire imbriqué et de la liste imbriquée):
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
«Comme j'écris actuellement une application de bureau pour mon travail avec Python, j'ai trouvé dans l'application de saisie de données quand il y a beaucoup d'entrées et que certaines ne sont pas obligatoires, l'utilisateur peut donc le laisser vide, à des fins de validation, il est facile de saisir toutes les entrées, puis supprimer la clé vide ou la valeur d'un dictionnaire. Donc, mon code ci-dessus montre comment nous pouvons facilement les supprimer, en utilisant la compréhension du dictionnaire et en conservant l'élément de valeur du dictionnaire qui n'est pas vide. J'utilise 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
Ainsi, la boucle et la suppression sont les plus rapides à 160ns, la compréhension de la liste est deux fois moins lente à ~ 375ns et avec un appel à dict()
est à nouveau deux fois moins lente à ~ 680ns.
L'emballage de 3 dans une fonction le ramène à environ 275 ns. Aussi pour moi PyPy était environ deux fois plus rapide que neet python.
list(dic.items())
py 3. Dict compréhension ftw alors? del semble toujours plus rapide pour un faible ratio de valeurs nulles / vides. Je suppose que la construction de cette liste est tout aussi mauvaise pour la consommation de mémoire que la simple recréation du dict.