2008-12-19 102 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,)))