2010-08-04 6 views
9

J'ai dû supprimer certains champs d'un dictionnaire, les clés de ces champs sont sur une liste. Alors j'écris cette fonction:Élégante façon de supprimer des champs de dictionnaires imbriqués

def delete_keys_from_dict(dict_del, lst_keys): 
    """ 
    Delete the keys present in the lst_keys from the dictionary. 
    Loops recursively over nested dictionaries. 
    """ 
    dict_foo = dict_del.copy()#Used as iterator to avoid the 'DictionaryHasChanged' error 
    for field in dict_foo.keys(): 
     if field in lst_keys: 
      del dict_del[field] 
     if type(dict_foo[field]) == dict: 
      delete_keys_from_dict(dict_del[field], lst_keys) 
    return dict_del 

Ce code fonctionne, mais ce n'est pas très élégant et je suis sûr que vous pouvez coder une meilleure solution.

+0

hmmm Je trouve élégant! –

+3

Je pense que ce n'est pas un mauvais code; vous avez le bit important qui est récursif sur les dictionnaires imbriqués. Vous devriez probablement vérifier 'isinstance (spam, collections.MutableMapping)' pour être plus polymorphe. – katrielalex

Répondre

15
def delete_keys_from_dict(dict_del, lst_keys): 
    for k in lst_keys: 
     try: 
      del dict_del[k] 
     except KeyError: 
      pass 
    for v in dict_del.values(): 
     if isinstance(v, dict): 
      delete_keys_from_dict(v, lst_keys) 

    return dict_del 
+1

Désolé mais ce code ne fonctionne pas comme prévu J'essaie de faire: imprimer delete_keys_from_dict ({'code': 'sdasda', 'tag.dbmko8e8': {'id': 'casas', 'nom': ' asdas identyfier '},' name ':' collection '}, ["id"]) Et supprimer tous les champs du dictionnaire :( – fasouto

+1

Je ne retournais pas le dictionnaire (j'ai mis à jour le code ci-dessus). Vous obteniez "Aucun" imprimé parce que la valeur n'a pas été retournée.Cette fonction ne modifie pas le dictionnaire, vous pouvez simplement imprimer le même dictionnaire que vous avez passé. J'ai mis à jour le code afin qu'il renvoie le dict aussi. –

+1

tbh Je pense que votre version fisrt était meilleure, ne pas retourner le dictionnaire car comme vous l'avez dit l'original aura déjà les clés mises à jour et vous ne perdez pas la valeur de retour pour retourner quelque chose qui existe déjà et la méthode dified à l'avenir pour renvoyer par exemple le nombre de valeurs supprimées sans modification du code appelant existant. – laurent

3

Puisque vous avez besoin déjà en boucle à travers tous les éléments du dict, je bâton avec une seule boucle et il suffit de l'utiliser pour un ensemble à la recherche les clés de supprimer

def delete_keys_from_dict(dict_del, the_keys): 
    """ 
    Delete the keys present in the lst_keys from the dictionary. 
    Loops recursively over nested dictionaries. 
    """ 
    # make sure the_keys is a set to get O(1) lookups 
    if type(the_keys) is not set: 
     the_keys = set(the_keys) 
    for k,v in dict_del.items(): 
     if k in the_keys: 
      del dict_del[k] 
     if isinstance(v, dict): 
      delete_keys_from_dict(v, the_keys) 
    return dict_del 
+0

@Ned Batchelder: est-il un moyen que nous pouvons inverser cela? Je veux dire seulement garder les clés spécifiques et supprimer le repos qui ne sont pas dans la liste? –

6

Puisque la question demandait une manière élégante, je soumettrais ma solution polyvalente aux structures imbriquées. Tout d'abord, installez le boltons utility package avec pip install boltons, puis:

from boltons.iterutils import remap 

data = {'one': 'remains', 'this': 'goes', 'of': 'course'} 
bad_keys = set(['this', 'is', 'a', 'list', 'of', 'keys']) 

drop_keys = lambda path, key, value: key not in bad_keys 
clean = remap(data, visit=drop_keys) 
print(clean) 

# Output: 
{'one': 'remains'} 

En bref, the remap utility est une approche complète, mais succincte à la manipulation des structures de données réelles qui sont souvent imbriquées, et peut même contenir des cycles et conteneurs spéciaux.

This page a beaucoup d'autres exemples, y compris ceux qui travaillent avec des objets beaucoup plus grands de l'API de Github.

C'est pur-Python, donc cela fonctionne partout, et est entièrement testé dans Python 2.7 et 3.3+. Le meilleur de tous, je l'ai écrit pour exactement les cas comme celui-ci, donc si vous trouvez un cas, il ne gère pas, vous pouvez me corriger pour le corriger right here.

+0

Neat! :) Je vous remercie. – darkless

0

En utilisant le code impressionnant de this poste et ajouter une petite déclaration:

def remove_fields(self, d, list_of_keys_to_remove): 
     if not isinstance(d, (dict, list)): 
      return d 
     if isinstance(d, list): 
      return [v for v in (self.remove_fields(v, list_of_keys_to_remove) for v in d) if v] 
     return {k: v for k, v in ((k, self.remove_fields(v, list_of_keys_to_remove)) for k, v in d.items()) if k not in list_of_keys_to_remove} 
0

Je pense que ce qui suit est plus élégante:

def delete_keys_from_dict(dict_del, lst_keys): 
    if not isinstance(dict_del, dict): 
     return dict_del 
    return {key:value for key,value in ((key, delete_keys_from_dict(value)) for key,value in dict_del.items()) if key not in lst_keys} 
Questions connexes