Как это сделать - Python словарь траверс и поиск
у меня есть вложенные словари:
{'key0': {'attrs': {'entity': 'p', 'hash': '34nj3h43b4n3', 'id': '4130'},
u'key1': {'attrs': {'entity': 'r',
'hash': '34njasd3h43b4n3',
'id': '4130-1'},
u'key2': {'attrs': {'entity': 'c',
'hash': '34njasd3h43bdsfsd4n3',
'id': '4130-1-1'}}},
u'key3': {'attrs': {'entity': 'r',
'hash': '34njasasasd3h43b4n3',
'id': '4130-2'},
u'key4': {'attrs': {'entity': 'c',
'hash': '34njawersd3h43bdsfsd4n3',
'id': '4130-2-1'}},
u'key5': {'attrs': {'entity': 'c',
'hash': '34njawersd3h43bdsfsd4n3',
'id': '4130-2-2'}}}},
'someohterthing': 'someothervalue',
'something': 'somevalue'}
предоставлена
6 ответов
Если вы хотите решить проблему в общем виде, независимо от того, сколько уровней вложенности у вас в dict, а затем создать рекурсивную функцию которая будет проходить елка:
def traverse_tree(dictionary, id=None):
for key, value in dictionary.items():
if key == 'id':
if value == id:
print dictionary
else:
traverse_tree(value, id)
return
>>> traverse_tree({1: {'id': 2}, 2: {'id': 3}}, id=2)
{'id': 2}
такая проблема часто лучше решается с помощью правильных определений классов, а не общих словарей.
class ProperObject( object ):
"""A proper class definition for each "attr" dictionary."""
def __init__( self, path, attrDict ):
self.path= path
self.__dict__.update( attrDict )
def __str__( self ):
return "path %r, entity %r, hash %r, id %r" % (
self.path, self.entity, self.hash, self.id )
masterDict= {}
def builder( path, element ):
masterDict[path]= ProperObject( path, element )
# Use the Visitor to build ProperObjects for each "attr"
walkDict( myDict, builder )
# Now that we have a simple dictionary of Proper Objects, things are simple
for k,v in masterDict.items():
if v.id == '4130-2-2':
print v
кроме того, теперь, когда у вас есть правильные определения объектов, вы можете сделать следующее
# Create an "index" of your ProperObjects
import collections
byId= collections.defaultdict(list)
for k in masterDict:
byId[masterDict[k].id].append( masterDict[k] )
# Look up a particular item in the index
print map( str, byId['4130-2-2'] )
Это старый вопрос, но все еще лучший результат google, поэтому я обновлю:
друг и я опубликовали библиотеку, чтобы решить (очень близко) эту точную проблему. dpath-python (не имеет отношения к модулю Perl dpath, который делает подобные вещи).
http://github.com/akesterson/dpath-python
все, что вам нужно сделать, это что-то вроде этого:
$ easy_install dpath
>>> import dpath.util
>>> results = []
>>> for (path, value) in dpath.util.search(my_dictionary, "*/attrs/entity/4130*", yielded=True):
>>> ... parent = dpath.util.search("/".join(path.split("/")[:-2])
>>> ... results.append(parent)
... это даст вам список всех объектов словаря, которые соответствует вашему поиску, т. е. всем объектам, которые имели (key = 4130*). Родительский бит немного нервный, но это сработает.
поскольку рекурсия, как известно, ограничена в python (см. какова максимальная глубина рекурсии в Python, и как ее увеличить?) Я бы предпочел ответить на этот вопрос на основе цикла, поэтому ответ может быть адаптирован к любому уровню глубины в словаре. Для этого функция
def walkDict( aDict, visitor, path=() ):
for k in aDict:
if k == 'attrs':
visitor( path, aDict[k] )
elif type(aDict[k]) != dict:
pass
else:
walkDict( aDict[k], visitor, path+(k,) )
можно заменить на:
def walkDictLoop(aDict, visitor, path=()):
toProcess = [(aDict, path)]
while toProcess:
dictNode, pathNode = toProcess.pop(0)
for k in dictNode:
if k == 'attrs':
visitor(pathNode, dictNode[k])
if isinstance(dictNode[k], dict):
toProcess.append( (dictNode[k], pathNode+(k,)) )
Ну, если вам нужно сделать это всего несколько раз, вы можете просто использовать вложенный dict.iteritems (), чтобы найти то, что вы ищете.
Если вы планируете сделать это несколько раз, выступления быстро становится проблемой. В этом случае вы могли бы:
измените способ возврата вам данных на что-то более подходящее.
Если вы не можете, преобразуйте данные один раз в дикт между id и ключами (используя iteritems). Тогда использовать он.