resueltos recorrer lista elementos ejercicios diccionarios diccionario dentro convertir anidados agregar python recursion dictionary traversal

recorrer - lista de diccionarios python



Encuentra todas las apariciones de una clave en diccionarios y listas de python anidados (8)

Tengo un diccionario como este:

{ "id" : "abcde", "key1" : "blah", "key2" : "blah blah", "nestedlist" : [ { "id" : "qwerty", "nestednestedlist" : [ { "id" : "xyz", "keyA" : "blah blah blah" }, { "id" : "fghi", "keyZ" : "blah blah blah" }], "anothernestednestedlist" : [ { "id" : "asdf", "keyQ" : "blah blah" }, { "id" : "yuiop", "keyW" : "blah" }] } ] }

Básicamente un diccionario con listas anidadas, diccionarios y cadenas, de profundidad arbitraria.

¿Cuál es la mejor manera de atravesar esto para extraer los valores de cada tecla "id"? Quiero lograr el equivalente de una consulta XPath como "// id". El valor de "id" es siempre una cadena.

Entonces, de mi ejemplo, el resultado que necesito es básicamente:

["abcde", "qwerty", "xyz", "fghi", "asdf", "yuiop"]

El orden no es importante.


Aquí está mi puñalada:

def keyHole(k2b,o): # print "Checking for %s in "%k2b,o if isinstance(o, dict): for k, v in o.iteritems(): if k == k2b and not hasattr(v, ''__iter__''): yield v else: for r in keyHole(k2b,v): yield r elif hasattr(o, ''__iter__''): for r in [ keyHole(k2b,i) for i in o ]: for r2 in r: yield r2 return

Ex.:

>>> findMe = {''Me'':{''a'':2,''Me'':''bop''},''z'':{''Me'':4}} >>> keyHole(''Me'',findMe) <generator object keyHole at 0x105eccb90> >>> [ x for x in keyHole(''Me'',findMe) ] [''bop'', 4]


Así es como lo hice.

Esta función busca de forma recursiva un diccionario que contiene diccionarios y listas anidados. Construye una lista llamada fields_found, que contiene el valor de cada vez que se encuentra el campo. El ''campo'' es la clave que estoy buscando en el diccionario y sus listas y diccionarios anidados.

def get_recursively(search_dict, field): """Takes a dict with nested lists and dicts, and searches all dicts for a key of the field provided. """ fields_found = [] for key, value in search_dict.iteritems(): if key == field: fields_found.append(value) elif isinstance(value, dict): results = get_recursively(value, field) for result in results: fields_found.append(result) elif isinstance(value, list): for item in value: if isinstance(item, dict): more_results = get_recursively(item, field) for another_result in more_results: fields_found.append(another_result) return fields_found


Encontré este Q / A muy interesante, ya que ofrece varias soluciones diferentes para el mismo problema. Tomé todas estas funciones y las probé con un objeto de diccionario complejo. Tuve que quitar dos funciones de la prueba porque tenían que fallar muchos resultados y no admitían la devolución de listas o dictados como valores, que considero esenciales, ya que una función debe prepararse para casi todos los datos que vendrán.

Así que bombeé las otras funciones en 100.000 iteraciones a través del módulo de tiempo y la salida llegó al siguiente resultado:

0.11 usec/pass on gen_dict_extract(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6.03 usec/pass on find_all_items(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.15 usec/pass on findkeys(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.79 usec/pass on get_recursively(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.14 usec/pass on find(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.36 usec/pass on dict_extract(k,o) - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Todas las funciones tenían la misma aguja para buscar (''logging'') y el mismo objeto de diccionario, que está construido así:

o = { ''temparature'': ''50'', ''logging'': { ''handlers'': { ''console'': { ''formatter'': ''simple'', ''class'': ''logging.StreamHandler'', ''stream'': ''ext://sys.stdout'', ''level'': ''DEBUG'' } }, ''loggers'': { ''simpleExample'': { ''handlers'': [''console''], ''propagate'': ''no'', ''level'': ''INFO'' }, ''root'': { ''handlers'': [''console''], ''level'': ''DEBUG'' } }, ''version'': ''1'', ''formatters'': { ''simple'': { ''datefmt'': "''%Y-%m-%d %H:%M:%S''", ''format'': ''%(asctime)s - %(name)s - %(levelname)s - %(message)s'' } } }, ''treatment'': {''second'': 5, ''last'': 4, ''first'': 4}, ''treatment_plan'': [[4, 5, 4], [4, 5, 4], [5, 5, 5]] }

Todas las funciones entregaron el mismo resultado, pero las diferencias de tiempo son dramáticas. La función gen_dict_extract(k,o) es mi función adaptada de las funciones aquí, en realidad es muy parecida a la función find de Alfe, con la diferencia principal, que estoy comprobando si el objeto dado tiene la función iteritems, en caso de que las cadenas sean pasado durante la recursión:

def gen_dict_extract(key, var): if hasattr(var,''iteritems''): for k, v in var.iteritems(): if k == key: yield v if isinstance(v, dict): for result in gen_dict_extract(key, v): yield result elif isinstance(v, list): for d in v: for result in gen_dict_extract(key, d): yield result

Entonces, esta variante es la más rápida y segura de las funciones aquí. Y find_all_items es increíblemente lento y está lejos del segundo get_recursivley más get_recursivley mientras que el resto, excepto dict_extract , está cerca el uno del otro. Las funciones fun y keyHole solo funcionan si buscas cadenas.

Interesante aspecto de aprendizaje aquí :)


Otra variación, que incluye la ruta anidada a los resultados encontrados ( nota: esta versión no tiene en cuenta las listas ):

def find_all_items(obj, key, keys=None): """ Example of use: d = {''a'': 1, ''b'': 2, ''c'': {''a'': 3, ''d'': 4, ''e'': {''a'': 9, ''b'': 3}, ''j'': {''c'': 4}}} for k, v in find_all_items(d, ''a''): print "* {} = {} *".format(''->''.join(k), v) """ ret = [] if not keys: keys = [] if key in obj: out_keys = keys + [key] ret.append((out_keys, obj[key])) for k, v in obj.items(): if isinstance(v, dict): found_items = find_all_items(v, key, keys=(keys+[k])) ret += found_items return ret


Solo quería repetir la excelente respuesta de @hexerei-software utilizando el yield from y aceptando listas de nivel superior.

def gen_dict_extract(var, key): if isinstance(var, dict): for k, v in var.items(): if k == key: yield v if isinstance(v, (dict, list)): yield from gen_dict_extract(v, key) elif isinstance(var, list): for d in var: yield from gen_dict_extract(d, key)


d = { "id" : "abcde", "key1" : "blah", "key2" : "blah blah", "nestedlist" : [ { "id" : "qwerty", "nestednestedlist" : [ { "id" : "xyz", "keyA" : "blah blah blah" }, { "id" : "fghi", "keyZ" : "blah blah blah" }], "anothernestednestedlist" : [ { "id" : "asdf", "keyQ" : "blah blah" }, { "id" : "yuiop", "keyW" : "blah" }] } ] } def findkeys(node, kv): if isinstance(node, list): for i in node: for x in findkeys(i, kv): yield x elif isinstance(node, dict): if kv in node: yield node[kv] for j in node.values(): for x in findkeys(j, kv): yield x print list(findkeys(d, ''id''))


d = { "id" : "abcde", "key1" : "blah", "key2" : "blah blah", "nestedlist" : [ { "id" : "qwerty", "nestednestedlist" : [ { "id" : "xyz", "keyA" : "blah blah blah" }, { "id" : "fghi", "keyZ" : "blah blah blah" }], "anothernestednestedlist" : [ { "id" : "asdf", "keyQ" : "blah blah" }, { "id" : "yuiop", "keyW" : "blah" }] } ] } def fun(d): if ''id'' in d: yield d[''id''] for k in d: if isinstance(d[k], list): for i in d[k]: for j in fun(i): yield j

>>> list(fun(d)) [''abcde'', ''qwerty'', ''xyz'', ''fghi'', ''asdf'', ''yuiop'']


def find(key, value): for k, v in value.iteritems(): if k == key: yield v elif isinstance(v, dict): for result in find(key, v): yield result elif isinstance(v, list): for d in v: for result in find(key, d): yield result