简体   繁体   English

使用Python中的键的有序列表在带有列表的嵌套字典中行走

[英]Walking down nested dictionary with list using an ordered list of keys in Python

I have a nested dict as follow: 我有一个嵌套的dict ,如下所示:

d = {'A': [{'B': [{'C': [{'D1':[]}, {'D2': []}]}]}]} # just an example

And I am given a list/path like this: 我得到这样的列表/路径:

l = ['A','B','C','D1']

I'd like to walk down the path in the dict to retrieve the corresponding value of D1 . 我想在dict沿路径检索D1的对应值。 I have written the following program to do that: 我编写了以下程序来做到这一点:

def find_dict(ld, key):
    # we can assume that 'ld' has dicts each of which has unique key
    for d in ld:
        if key not in d:
            continue
        return d

def walk_dict(d,path):
    temp = None
    for i,n in enumerate(path):
        if i == 0:
            temp = d.get(n)
        elif i < (len(path)-1):
            temp = find_dict(temp, n)
            temp = temp.get(n)
        else: # last item
            temp = find_dict(temp, n)
            print('Found it!')
            print(i,n)
            print(temp.get(n))
            return temp.get(n)

d = {'A': [{'B': [{'C': [{'D1':[]}, {'D2': []}]}]}]}
l = ['A','B','C','D1'] # successfully retrieved the value of 'D1'
walk_dict(d,l)
l = ['A','B','C','D2'] # successfully retrieved the value of 'D2'
walk_dict(d,l)
l = ['A','B','C','D3'] # get NoneType error as expected because 'D3' does not exist
walk_dict(d,l)

I'd like to know if there's a better way to accomplish this. 我想知道是否有更好的方法可以做到这一点。 I feel like this approach has a bit too verbose, and maybe even have bug(s) lurking. 我觉得这种方法太冗长了,甚至可能还潜伏着错误。

Thank you in advance for your answers/suggestions! 预先感谢您的回答/建议!

I have used a recursive approach. 我使用了递归方法。

d = {'A': [{'B': [{'C': [{'D1':'xyz'}, {'D2': []}]}]}]}
l = ['A','B','C','D1']
output = []
def find_path(dict_obj,key=0):
    for k,v in dict_obj.items():
        if k == l[key]:
            if key == len(l)-1:
                output.append(v)
                return
            if isinstance(v,dict):
                find_path(v, key+1)
            elif isinstance(v,list):
                for i,item in enumerate(v):
                    if isinstance(item,dict):
                        find_path(item,key+1)

find_path(d)
print(output[0] if output else None)

Outputs: 输出:

l = ['A','B','C','D1']
# xyz
l = ['A','B','C']
# [{'D1': 'xyz'}, {'D2': []}]
l = ['A','B','C','D3']
# None
l = ['A','B','C','D2']
# []

How about walk the dict like this: 如何像这样走动字典:

from copy import deepcopy

def walk_dict(ld, path):
    if isinstance(path, str):
        path = path.split()
    t = deepcopy(ld)
    for key in path:
        if isinstance(t, list):
            try:
                t = [d for d in t if key in d][0]
            except IndexError:
                return
        try:
            t = t[key]
        except KeyError:
            return
    else:
        return t

def main():
    d = {'A': [{'B': [{'C': [{'D1':['values of D1']}, {'D2': "I'm D2"}]}]}]}
    a = 'A B C D1'
    print(walk_dict(d, a))
    a = 'A B C D2'
    print(walk_dict(d, a))
    a = 'A B C D3'
    print(walk_dict(d, a))
    a = 'A B1 C D3'
    print(walk_dict(d, a))


if __name__ == '__main__':
    main()

Output: 输出:

['values of D1']
I'm D2
None
None

You can use this recursive function: 您可以使用此递归函数:

d = {'A': [{'B': [{'C': [{'D1':[]}, {'D2': []}]}]}]} # just an example
def walk(_d, path):
   a, *b = path
   if isinstance(_d, list):
     _d = [i for i in _d if a in i]
     if not _d:
       return None
     _d = _d[0]
   return _d[a] if not b else walk(_d[a], b)

tests = [[['A','B','C','D1'], []], [['A','B','C','D2'], []], [['A','B','C','D3'], None]]
for a, b in tests:
  assert walk(d, a) == b

print('all cases passed')

Output: 输出:

all cases passed

Below are the timings for the current answers. 以下是当前答案的时间安排。 The source code for the timings and input setup can be found as a gist here . 时序和输入设置的源代码可以在这里找到要点。

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM