2010-08-04 12 views
9

Ho dovuto rimuovere alcuni campi da un dizionario, le chiavi di questi campi sono in un elenco. Così scrivo questa funzione:Modo elegante per rimuovere i campi dai dizionari nidificati

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 

questo codice funziona, ma non è molto elegante e sono sicuro che è possibile codificare una soluzione migliore.

+0

hmmm Trovo elegante! –

+3

Penso che non sia male il codice; hai il bit importante che ricorre su dizionari nidificati. Probabilmente dovresti controllare 'isinstance (spam, collections.MutableMapping)' per essere più polimorfico. – katrielalex

risposta

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

Scusa ma questo codice non funziona come previsto, provo a fare: print delete_keys_from_dict ({'code': 'sdasda', 'tag.dbm88e8': {'id': 'casas', 'name': ' asdas identyfier '},' name ':' collection '}, ["id"]) E cancellare tutti i campi dal dizionario :( – fasouto

+1

Non stavo restituendo il dizionario (ho aggiornato il codice sopra). Stava ottenendo "Nessuno" stampato perché il valore non veniva restituito Poiché questa funzione non modifica il dizionario, puoi semplicemente stampare lo stesso dizionario che hai passato. Ho aggiornato il codice in modo che restituisca anche il dett. –

+1

tbh Penso che la tua versione originale fosse migliore, non restituendo il dizionario perché come hai detto l'originale avrà già le chiavi aggiornate e non stai "sprecando" il valore restituito per restituire qualcosa già esistente e il metodo potrebbe essere mo Diffezionato in futuro per restituire ad esempio il numero di valori rimossi senza modifiche al codice chiamante già esistente. – laurent

3

Dal momento che già bisogno di scorrere ogni elemento del dizionario, mi piacerebbe restare con un singolo ciclo e solo assicurarsi di utilizzare un set per la ricerca delle chiavi per eliminare

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: c'è un modo per invertire questo? Intendo solo mantenere le chiavi specifiche e cancellare il resto che non sono nella lista? –

6

Dato che la domanda ha richiesto un modo elegante, presenterò la mia soluzione generica per organizzare strutture annidate. In primo luogo, installare il boltons utility package con pip install boltons, quindi:

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'} 

In breve, the remap utility è un approccio full-optional, ma succinta a gestire strutture di dati del mondo reale che sono spesso annidati, e può anche contenere cicli e contenitori speciali

This page ha molti altri esempi, inclusi quelli che lavorano con oggetti molto più grandi dell'API di Github.

È puro Python, quindi funziona ovunque ed è completamente testato in Python 2.7 e 3.3+. Meglio di tutti, l'ho scritto per casi esattamente come questo, quindi se trovi un caso che non gestisce, puoi mandarmi un bug per sistemarlo right here.

+0

Neat! :) Grazie. – darkless

0

Utilizzando il codice impressionante da this postale e aggiungere una piccola dichiarazione:

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

Penso che la segue è più elegante:

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} 
Problemi correlati