2008-12-19 41 views
9

Tôi đã lồng:Làm thế nào để làm điều này - từ điển python traverse từ điển và tìm kiếm

{'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'} 

đưa ra một id - một trong tất cả các ids như 4130 để 4130-2-2.
cách dễ nhất để điều hướng đến từ điển chính xác là gì?

Giống như nếu đưa id4130-2-1 sau đó nó phải đạt từ điển với key=key5

phi xml tiếp cận xin vui lòng.

Chỉnh sửa (1): Làm tổ nằm giữa các mức 1 đến 4, nhưng tôi biết cách làm tổ trước khi phân tích cú pháp.

Chỉnh sửa (2): Đã sửa mã.

** Chỉnh sửa (3): ** Mã cố định một lần nữa cho các giá trị chuỗi là ids. Xin vui lòng tha cho sự nhầm lẫn tạo ra. Điều này là cuối cùng tôi hy vọng :)

+0

cho '4130-2-1' bạn muốn 'key4', không 'key5' phải không? 'key5' có vẻ chứa '4130-2-2'. –

+0

** Xem thêm: ** https://stackoverflow.com/questions/7681301/search-for-a-key-in-a-nested-python-dictionary https://stackoverflow.com/a/16508328/42223 – dreftymac

Trả lời

14

Cấu trúc của bạn là không thường xuyên bất thường. Đây là phiên bản có chức năng Lượt truy cập di chuyển ngang qua các từ điển phụ 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 

Bạn sẽ sử dụng nó như thế này.

walkDict(myDict, filterFor) 

này có thể được biến thành một máy phát điện thay vì một khách; nó sẽ yield path, aDict[k] thay vì gọi hàm khách truy cập.

Bạn sẽ sử dụng nó trong vòng lặp for.

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

Tôi có một bộ sưu tập khổng lồ, nếu bạn có thể đề xuất một cấu trúc tốt hơn với sự hỗ trợ mức tùy ý, dễ dàng chèn và lấy, điều đó sẽ rất tuyệt vời. Vào thời điểm bạn tìm ra cấu trúc đó, tôi sẽ thử giải pháp của bạn. Cảm ơn. –

+3

@JV: Từ điển "attrs" nội bộ không được thông báo. Đó là một ứng cử viên để trở thành đối tượng của một số lớp được xác định, không chỉ là từ điển ẩn danh. –

+0

+1 để sử dụng Khách truy cập –

0

Vâng, nếu bạn phải làm điều đó chỉ một vài lần, bạn chỉ có thể sử dụng lồng nhau dict.iteritems() để tìm thấy những gì bạn đang tìm kiếm.

Nếu bạn định làm điều đó nhiều lần, buổi biểu diễn sẽ nhanh chóng trở thành một vấn đề. Trong trường hợp đó, bạn có thể:

  • thay đổi cách dữ liệu của bạn được trả về cho bạn cho phù hợp hơn.

  • nếu bạn không thể, chuyển đổi dữ liệu sau khi bay đến một dict giữa id và phím (sử dụng iteritems). Sau đó sử dụng nó.

+0

ý tưởng khi chúng ta tạo cấu trúc này là truy cập nó thông qua các khóa - như - key1, key2, vv Bây giờ tôi vấp phải một yêu cầu để truy cập vào các id. Tuy nhiên, điểm đạn thứ hai là một gợi ý hay, sẽ thử điều đó. –

12

Nếu bạn muốn giải quyết vấn đề một cách tổng quát, dù có bao nhiêu mức độ làm tổ mà bạn có trong dict của bạn, sau đó tạo ra một hàm đệ quy mà sẽ đi qua cây:

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

Điều này không hoạt động khi tôi thử trên máy tính của mình. – PEZ

+0

Tôi đã sửa mã ví dụ được đề cập, vui lòng xem lại –

+0

Tôi đã bỏ phiếu cho bạn, không biết cách chọn 2 câu trả lời nếu không tôi cũng đã chọn mã này. :) –

9

Loại sự cố này thường được giải quyết tốt hơn với định nghĩa lớp thích hợp, không phải từ điển chung chung.

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 

Ngoài ra, bây giờ mà bạn có định nghĩa đối tượng đúng cách, bạn có thể làm như sau

# 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']) 
+0

Nếu bạn thực hiện rất nhiều lần tra cứu, chi phí để chuyển đổi thành Đối tượng và sau đó đến chỉ mục trên 'id' được phân bổ theo các lần tra cứu. Xây dựng các đối tượng là O (n). Xây dựng chỉ mục là O (n) và có thể được thực hiện khi các đối tượng đang được xây dựng. Tra cứu trên id là O (1). –

4

Đây là một câu hỏi cũ nhưng vẫn là một kết quả google hàng đầu, vì vậy tôi sẽ cập nhật:

Một người bạn và bản thân tôi đã xuất bản một thư viện để giải quyết (rất gần) vấn đề chính xác này. dpath-python (không liên quan đến mô-đun dpath perl làm những việc tương tự).

http://github.com/akesterson/dpath-python

Tất cả các bạn sẽ cần phải làm là một cái gì đó như thế này:

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

... rằng sẽ cung cấp cho bạn một danh sách của tất cả các đối tượng từ điển khớp với tìm kiếm của bạn, ví dụ, tất cả các các đối tượng có (key = 4130 *). Bit gốc là một chút janky, nhưng nó sẽ làm việc.

+0

Đây là một thư viện tuyệt vời. Điều này xứng đáng được chú ý nhiều hơn. – dreftymac

1

Vì đệ quy được biết là bị giới hạn ở trăn (xem What is the maximum recursion depth in Python, and how to increase it?) Tôi muốn có câu trả lời dựa trên vòng lặp cho câu hỏi này, vì vậy câu trả lời có thể được điều chỉnh theo bất kỳ mức độ sâu nào trong từ điển. Cho rằng, hàm

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

có thể được thay thế bằng:

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,))) 
Các vấn đề liên quan