2008-12-19 107 views
9

我有嵌套的字典:如何做到这一点 - 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'} 

给予id - 所有ids41304130-2-2之一。
什么是导航到正确字典的最简单方法?

一样,如果给定的id4130-2-1,那么它应该达到与key=key5

非XML字典方法请。

编辑(1):嵌套之间的14水平,但我知道我解析之前的嵌套。

编辑(2):修正了代码。

**编辑(3):**再次为字符串值ids重新固定代码。请原谅造成的混乱。这是最后我希望:)

+0

为 '4130-2-1' 你想 '键4',而不是 'KEY5' 吧? 'key5'似乎包含'4130-2-2'。 – 2014-06-26 23:27:05

+0

**另请参阅:** https://stackoverflow.com/questions/7681301/search-for-a-key-in-a-nested-python-dictionary https://stackoverflow.com/a/16508328/42223 – dreftymac 2017-10-30 19:55:16

回答

14

你的结构令人不愉快地不规则。这是一个访问者函数,该函数遍历attrs子字典。

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 printMe(path, element): 
    print path, element 

def filterFor(path, element): 
    if element['id'] == '4130-2-2': 
     print path, element 

你会这样使用它。

walkDict(myDict, filterFor) 

这可以变成一个发生器,而不是一个访客;它会yield path, aDict[k]而不是调用访问者功能。

你可以在for循环中使用它。

for path, attrDict in walkDictIter(aDict): 
    # process attrDict... 
0

那么,如果你只需要做几次,你可以使用嵌套dict.iteritems()来找到你要找的东西。

如果你打算做几次,表演很快就会成为一个问题。在这种情况下,您可以:

  • 更改数据返回给您的方式,使其更适合您。

  • 如果你不能,将数据转换为id和keys之间的字典(使用iteritems)。然后使用它。

+0

当我们创建这个结构时,想法是通过键来访问它 - 就像 - key1,key2等一样。现在我偶然发现了访问通过id的需求。第二个要点是一个很好的建议,但会尝试。 – 2008-12-19 12:23:05

12

如果你想解决一般的方式问题,无论嵌套你在你的字典中有多少水平,然后创建一个递归函数将遍历树:

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} 
+0

当我在我的机器上尝试时,这不起作用。 – PEZ 2008-12-19 12:06:24

+0

我修复了有问题的示例代码请重新看一下 – 2008-12-19 12:18:54

+0

我投你了票,不知道如何选择2个答案,否则我会选择这一个。 :) – 2008-12-19 13:18:25

9

这种问题通常用适当的类定义解决,而不是通用字典。

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']) 
4

这是一个老问题,但仍然是一个顶级谷歌的结果,所以我会更新:

我和一个朋友出版了一个图书馆来解决(非常接近)这个确切的问题。 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) 

...这会给你所有符合您搜索的字典对象的列表,即所有有(键= 4130 *)的对象。父母的位有点难受,但它会起作用。

1

由于递归被称为Python中的限制(见 What is the maximum recursion depth in Python, and how to increase it?) 我宁愿有一个循环基于这个问题的答案,所以答案可以适用于深度的字典中的任何水平。为此,该函数

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,)))