2010-08-04 17 views
9

Tuve que eliminar algunos campos de un diccionario, las claves de estos campos están en una lista. Así que escribo esta función:Manera elegante de eliminar campos de diccionarios anidados

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 

Este código funciona, pero no es muy elegante y estoy seguro de que se puede codificar una mejor solución.

+0

hmmm me resulta elegante! –

+3

Creo que no es un código malo; tienes el bit importante que está recurriendo a los diccionarios anidados. Probablemente deberías comprobar 'isinstance (spam, collections.MutableMapping)' para que sea más polimórfico. – katrielalex

Respuesta

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

Lo siento pero este código no funciona como esperaba Lo intenté hacer: print delete_keys_from_dict ({'code': 'sdasda', 'tag.dbmko8e8': {'id': 'casas', 'name': ' asdas identyfier '},' name ':' collection '}, ["id"]) Y elimine todos los campos del diccionario :( – fasouto

+1

No devolví el diccionario (he actualizado el código anterior). Le imprimieron "Ninguno" porque no se devolvió el valor. Como esta función no modifica el diccionario, puede simplemente imprimir el mismo diccionario que ingresó. He actualizado el código para que también devuelva el dict. –

+1

tbh Creo que su versión fisrt fue mejor, no devolvió el diccionario porque como dijo el original ya tendrá las claves actualizadas y no está "desperdiciando" el valor de retorno para devolver algo que ya existe y el método podría ser mo En el futuro, se difiere para devolver, por ejemplo, el número de valores eliminados sin cambios en el código de llamada ya existente. – laurent

3

Puesto que ya necesita para recorrer todos los elementos de la dict, me quedo con un solo bucle y sólo asegúrese de usar un conjunto para buscar las claves para eliminar

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: ¿hay alguna manera de revertir esto? Me refiero solo a mantener las teclas específicas y eliminar el resto que no están en la lista? –

6

Como la pregunta fue solicitada de una manera elegante, presentaré mi solución de propósito general para disputar estructuras anidadas. En primer lugar, instale el boltons utility package con pip install boltons, entonces:

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 resumen, the remap utility es un enfoque completamente equipado, aunque sucinta a la manipulación de estructuras de datos del mundo real que a menudo están anidados, e incluso puede contener ciclos y contenedores especiales.

This page tiene muchos más ejemplos, incluidos los que trabajan con objetos mucho más grandes de la API de Github.

Es puro-Python, por lo que funciona en todas partes, y está completamente probado en Python 2.7 y 3.3+. Lo mejor de todo es que lo escribí exactamente para casos como este, así que si encuentras un caso que no funciona, puedes echarme una broma para arreglarlo right here.

+0

¡Aseado! :) Gracias. – darkless

0

Utilizando el código impresionante desde this posterior y añadir una pequeña declaración:

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

Creo que el siguiente es más 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} 
Cuestiones relacionadas