使用python3以特定顺序迭代嵌套dict

时间:2017-01-19 13:22:24

标签: python python-3.x dictionary

我有一个嵌套的dict,其中包含列表和dict,如下所示。

m = {'abc': 
      {'bcd': [
               {'cde':'100','def':'200','efg':'300'},
               {'cde':'3000','def':'500','efg':'4000'}
              ], 
       'ghi': 
         {'mnc': [
                  {'xyz':'8827382','mnx':'e838','wyew':'2232'}
                 ]
         }
       }
     }

我的要求是匹配mnx密钥,如果值为'e838',则获取该特定字典中其他密钥的值。因此,从上面的例子中我可能需要xyz键的值。

为此,我创建了一个递归循环函数,如下所示,它正在工作。不过我的问题是,是否有更好/更简单的方法来做到这一点。如果我需要使用密钥mnx获取所有值,那么在同一代码中可以做什么。谢谢。

注意:我在jxmlease lib的帮助下将XML转换为dict。

def iterate_dict(dict1,key1,val1):
    for key, value in dict1.items():
        if key == key1 and value == val1:
            return dict1
        if isinstance(value,list):
            for item1 in value:
                if isinstance(item1,dict):
                    for k,v in item1.items():
                        if k == key1 and v == val1:
                            return item1
        if isinstance(value,dict):
            for key,var in value.items():
                if key == key1 and var == val1:
                    return value
                else:
                    return iterate_dict(value,key1,val1)

3 个答案:

答案 0 :(得分:1)

您可以排序将该词典“压扁”成一个词典列表,然后根据需要进行查询:

def flatten_dict(d):
    flattened = []
    current = {}
    for k, v in d.items():
        if isinstance(v, dict):
            flattened.extend(flatten_dict(v))
        elif isinstance(v, list):
            flattened.extend(sum((flatten_dict(v_d) for v_d in v), []))
        else:
            current[k] = v
    if len(current) > 0:
        flattened = [current] + flattened
    return flattened

def values_in_flattened(flattened, key):
    return list(filter(None, (d.get(key, None) for d in flattened))) or None

m = {'abc': {'bcd':[{'cde':'100','def':'200','efg':'300'},{'cde':'3000','def':'500','efg':'4000'}], 'ghi':{'mnc':[{'xyz':'8827382','mnx':'e838','wyew':'2232'}]}}}
mf = flatten_dict(m)
efg_vals = values_in_flattened(mf, 'efg')
print(mf)
print(efg_vals)

>>>
[{'xyz': '8827382', 'mnx': 'e838', 'wyew': '2232'}, {'def': '200', 'efg': '300', 'cde': '100'}, {'def': '500', 'efg': '4000', 'cde': '3000'}]
['300', '4000']

答案 1 :(得分:0)

m['abc']['bcd'] + m['abc']['ghi']['mnc']

出:

[{'cde': '100', 'def': '200', 'efg': '300'},
 {'cde': '3000', 'def': '500', 'efg': '4000'},
 {'mnx': 'e838', 'wyew': '2232', 'xyz': '8827382'}]

你应该建立一个dict列表来迭代,而不是使用原始数据。

答案 2 :(得分:0)

此代码使用递归生成器进行搜索,因此它会在找到它们时生成所有解决方案。

iterate_dict找到带有所需(键,值)对的dict时,它会调用filter_dict,这会创建一个包含输出的新dict。这个新的dict包含传递给filter_dict的dict的项目,除了它过滤掉所需的(键,值)对,它还过滤掉了dict可能包含的任何列表或词典。但是,iterate_dict将递归处理那些寻找进一步匹配的列表或词。如果您不希望iterate_dict查找更多匹配项,则可以轻松修改代码,以便它不会执行此操作;请看下面。

如果要搜索包含所需键的dicts并且不关心与该键关联的值,可以将None作为val参数传递,或者只省略该arg。

我已经稍微修改了你的数据,所以我们可以测试递归搜索包含匹配的字典中的进一步匹配。

def filter_dict(d, key):
    return {k: v for k, v in d.items() 
        if k != key and not isinstance(v, (dict, list))}

def iterate_dict(d, key, val=None):
    if key in d and (val is None or d[key] == val):
        yield filter_dict(d, key)
    yield from iterate_list(d.values(), key, val)

def iterate_list(seq, key, val):
    for v in seq:
        if isinstance(v, list):
            yield from iterate_list(v, key, val)
        elif isinstance(v, dict):
            yield from iterate_dict(v, key, val)

# test

data = {
    'abc': {
        'bcd': [
            {'cde':'100', 'def':'200', 'efg':'300'},
            {'cde':'3000', 'def':'500', 'efg':'4000'},
            {'abc': '1', 'mnx': '2', 'ijk': '3', 
                'zzz': {'xyzz':'44', 'mnx':'e838', 'yew':'55'}
            },
        ], 
        'ghi': {
            'mnc': [
                {'xyz':'8827382', 'mnx':'e838', 'wyew':'2232'}
            ]
        }
    }
}

for d in iterate_dict(data, 'mnx', 'e838'):
    print(d)

<强>输出

{'yew': '55', 'xyzz': '44'}
{'xyz': '8827382', 'wyew': '2232'}

这是一个搜索,查找包含'mnx'键的所有词组:

for d in iterate_dict(data, 'mnx'):
    print(d)

<强>输出

{'ijk': '3', 'abc': '1'}
{'xyzz': '44', 'yew': '55'}
{'wyew': '2232', 'xyz': '8827382'}

如果您希望在找到匹配项后递归搜索每个字典以进一步匹配,只需将iterate_dict更改为:

def iterate_dict(d, key, val=None):
    if key in d and (val is None or d[key] == val):
        yield filter_dict(d, key)
    else:
        yield from iterate_list(d.values(), key, val)