繁体   English   中英

从字典中删除额外的嵌套

[英]Remove extra nesting from dictionary

我有这本字典:

mydict = {'KEY': {'KEY': {'KEY': {'KEY': ['mylist']}}}}

是否有任何 pythonic 方法来删除嵌套键并获得最后一项获得此:

{'KEY': ['mylist']}

无需通过以下项目一一手动 go:

def simplify_dict(d: dict) -> dict:
    for k, v in d.items():
        for k1, v1 in v.items():
            for k2, v2 in v1.items():
                for k3, v3 in v2.items():
                    return {k: v3}

这是一个递归 function ,它也对输入字典进行一些基本检查。

def reduce_dict(d):
    for k,v in d.items():
        if isinstance(v, dict) \
        and len(v) == 1 \
        and k in v.keys():
            return reduce_dict(v)
    return d


mydict = {'KEY': {'KEY': {'KEY': {'KEY': ['mylist']}}}}

print(reduce_dict(mydict))
#{'KEY': ['mylist']}

一种递归解决方案,使用defaultdict构建具有相同键的所有字典值的列表。 出于演示目的,我在您的字典中添加了一些额外的值。

>>> mydict = {'KEY': {'KEY': {'KEY': {'KEY': ['mylist'], 'name': 'foo'}, 'name': 'bar'}}}
>>> def flatten_dict(d, results=defaultdict(list)):
...     for k, v in d.items():
...         if isinstance(v, dict):
...             flatten_dict(v, results)
...         else:
...             results[k].append(v)
...     return dict(results)
...
>>> flatten_dict(mydict)
{'KEY': [['mylist']], 'name': ['foo', 'bar']}
>>>

另一个练习是向flatten_dict添加一个levels参数,以可能限制它递归的级别数。


现在,如果您只是在寻找嵌套最深的字典,我们可以使用不同的递归 function 来遍历字典mydict和 map 对每个嵌套字典的depth数。 然后我们只需要遍历该列表并找到具有最大数字的字典。

这个想法的中间实现,显示了包含每个字典及其深度的列表。

>>> def deepest_dict(d, result=[], depth=0):
...     result.append((depth, d))
...     for k, v in d.items():
...         if isinstance(v, dict):
...             deepest_dict(v, result, depth+1)
...     return result
...
>>> deepest_dict(mydict)
[(0, {'KEY': {'KEY': {'KEY': {'KEY': ['mylist'], 'name': 'foo'}, 'name': 'bar'}}}), (1, {'KEY': {'KEY': {'KEY': ['mylist'], 'name': 'foo'}, 'name': 'bar'}}), (2, {'KEY': {'KEY': ['mylist'], 'name': 'foo'}, 'name': 'bar'}), (3, {'KEY': ['mylist'], 'name': 'foo'})]
>>>

以及完整的实现,只是过滤掉我们想要的东西。

>>> def deepest_dict(d, result=[], depth=0):
...     result.append((depth, d))
...     for k, v in d.items():
...         if isinstance(v, dict):
...             deepest_dict(v, result, depth+1)
...     max_depth = max(depth for depth, _ in result)
...     return [elem for depth, elem in result if depth == max_depth]
...
>>>
>>> deepest_dict(mydict)
[{'KEY': ['mylist'], 'name': 'foo'}]
>>>

您可以使用递归生成器来生成项目。 由于您的示例有点模棱两可(非分支),我根据预期的 output (单个项目或分组项目)提供两种不同的解决方案:

使用的输入:

mydict = {'KEY': {'KEY': {'KEY': {'KEY': ['mylist'],
                                  'KEY3': 'abc',
                                  'KEY4': 'def'}},
                  'KEY2': '123'}}
单品
def unnest(d):
    for k,v in d.items():
        if isinstance(v, dict):
            for x in unnest(v):
                yield x
        else:
            yield {k:v}
            
list(unnest(mydict))
# [{'KEY': ['mylist']}, {'KEY3': 'abc'}, {'KEY4': 'def'}, {'KEY2': '123'}]
分组项目
def unnest(d):
    out = {}
    for k,v in d.items():
        if isinstance(v, dict):
            for x in unnest(v):
                yield x
        else:
            out[k] = v
    if out:
        yield out
            
list(unnest(mydict))
# [{'KEY': ['mylist'], 'KEY3': 'abc', 'KEY4': 'def'}, {'KEY2': '123'}]
作为平面词典
{k:v for d in unnest(mydict) for k,v in d.items()}
# {'KEY': ['mylist'], 'KEY3': 'abc', 'KEY4': 'def', 'KEY2': '123'}

如果您知道键并且最终值不是字典:

d = {'KEY': {'KEY': {'KEY': {'KEY': ['mylist']}}}}
while isinstance(d, dict):
    d = d['KEY']
{'KEY': d}
#{'KEY': ['mylist']}

假设您要查找的是由层次结构中的叶字典形成的字典,您可以通过逐步将包含字典的键替换为该字典的内容来迭代地执行此操作:

mydict = {'KEY': 
             {'KEY': 
                {'KEY': 
                   {'KEY': ['mylist']}
                },
              'KEY2':
                   {'KEY3':[1,2,3]}
             },
          'KEY4':[7,8]
         }

while dict in map(type,mydict.values()):
    subKeys = [k for k in mydict if isinstance(mydict[k],dict)]
    mydict.update([kv for s in subKeys for kv in mydict.pop(s).items()])
    
print(mydict)
{'KEY4': [7, 8], 'KEY3': [1, 2, 3], 'KEY': ['mylist']}

如果您希望它作为新字典(而不是就地字典),递归 function 可能是实现它的最优雅的方式:

def leaf(D):
    return { k:v for s,d in D.items()
             for k,v in (leaf(d).items() if isinstance(d,dict) else [(s,d)]) }

print(leaf(mydict))
{'KEY': ['mylist'], 'KEY3': [1, 2, 3], 'KEY4': [7, 8]}

如果您可以使用库,可以使用 ChainMap 来实现递归 function:

from collections import ChainMap
def leaf(D):
    return dict(ChainMap(*(leaf(d) if type(d)==dict else {k:d}
                           for k,d in D.items())))

暂无
暂无

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

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