J'ai un dict et je voudrais supprimer toutes les clés pour lesquelles il existe des chaînes de valeur vides.
metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}
Quelle est la meilleure façon de procéder?
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’existe pas de clé dans un dict sans valeur; s'il n'avait pas de valeur, ce ne serait pas dans le dict.
Cela peut même être 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.
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 le parcourant (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 que de nombreuses entrées ne contiennent des valeurs vides.
Si vous souhaitez une approche complète, mais succincte, de la gestion de structures de données réelles souvent imbriquées et pouvant même contenir des cycles, je vous recommande de regarder l'utilitaire de remappage du paquetage utilitaire de boltons .
Après pip install boltons
ou après avoir copié 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 du pur Python, il fonctionne donc partout et est entièrement testé en Python 2.7 et 3.3+. Le meilleur de tout, je l'ai écrit pour des cas comme celui-ci, donc si vous trouvez un cas qu'il ne gère pas, vous pouvez me déranger de le résoudre ici même .
La solution de BrenBarn est idéale (et Pythonic, pourrais-je ajouter). Voici une autre solution (fp), cependant:
from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))
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
Si vous avez un dictionnaire imbriqué et que vous voulez que cela fonctionne même pour les 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
S'appuyant sur les réponses de patriciasz et nneonneo , et en tenant compte de la possibilité que vous souhaitiez supprimer des clés contenant uniquement certaines faussetés (par exemple ''
) mais pas d'autres (par exemple, 0
) Même si vous souhaitez inclure des éléments véridiques (par exemple, 'SPAM'
), vous pouvez créer une liste de résultats hautement spécifique:
unwanted = ['', u'', None, False, [], 'SPAM']
Malheureusement, cela ne fonctionne pas tout à fait, car par exemple 0 in unwanted
est évalué à True
. Nous devons faire la distinction entre 0
et d’autres choses fausses, nous devons donc utiliser is
:
any([0 is i for i in unwanted])
... est évalué à 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 modifier metadata
à la place:
newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}
### 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)
Une autre façon de procéder consiste à utiliser la compréhension du dictionnaire. Cela devrait être compatible avec 2.7+
result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}
J'ai lu toutes les réponses dans ce fil et certaines se sont référées également à ce fil: Supprimer les signes vides du dictionnaire imbriqué avec une fonction récursive
J'ai initialement utilisé la solution ici et cela a très bien fonctionné:
Tentative 1: Trop chaud (pas performant ou à 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 quelques problèmes de performances et de compatibilité ont été soulevés dans le monde Python 2.7:
isinstance
au lieu de type
for
pour plus d'efficacitéitems
au lieu de iteritems
Tentative 2: Trop froid (manque de mémoisation):
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 ce qu'il faut (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
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 sont ignorées s'il existe des entiers et des valeurs float 0 et 0.0
Si quelqu'un veut éviter ce qui précède peut utiliser le code ci-dessous (supprime les chaînes vides et les valeurs None du dictionnaire et de la liste imbriqués):
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