如何将此扁平字典转换为嵌套字典?

时间:2018-12-26 02:48:32

标签: python

有一个简单的字典,像这样:

{"a.b": "foo", "a.c": "bar", "d.e.f":"baz"}

以及如何使用Python将dict传递到此:

{
    "a": 
        {
            "b": "foo",
            "c": "bar"
        },
    "d": 
        {
            "e":
                {
                    "f": "baz"
                }
        }
}

2 个答案:

答案 0 :(得分:3)

您可以使用以下方法将split上的键.转换为最后一个值和之前的所有内容:

*parents, key =  k.split('.')

您可以使用类似以下内容来挖掘嵌套字典(如果需要,可以创建它们):

end = reduce(lambda x, y: x.setdefault(y, {}) , parents, new_d) 

此处end将是链末尾的字典。因此,此时您只需分配值即可。像这样:

from functools import reduce

d = {"a.b": "foo", "a.c": "bar", "d.e.f":"baz", "d.e.g":"M", 'l':"single_key_test" }

new_d = {}

for k in d:
    *parents, key =  k.split('.')
    end = reduce(lambda x, y: x.setdefault(y, {}) , parents, new_d)
    end[key] = d[k]

print(new_d)

结果:

{'a': {'b': 'foo', 'c': 'bar'}, 'd': {'e': {'f': 'baz', 'g': 'M'}}, 'l': 'single_key_test'}

答案 1 :(得分:1)

首先,您必须用'.'分割密钥以获取路径。

然后,您可以创建一个从一个路径创建嵌套字典的函数:

def make_nested_dict(iterable, final):
    """Make nested dict from iterable"""
    if iterable:
        head, *tail = iterable
        return {head: make_nested_dict(tail, final)}
    else:
        return final

其工作方式如下:

d = {"a.b": "foo", "a.c": "bar", "d.e.f":"baz"}    

for key in d:
    paths = key.split('.')
    nested_path = make_nested_dict(paths, d[key])
    print(nested_path)

并给出以下路径:

{'a': {'b': 'foo'}}
{'a': {'c': 'bar'}}
{'d': {'e': {'f': 'baz'}}}

然后,您可以创建一个将两个嵌套字典递归合并的函数:

def merge_nested_dicts(d1, d2):
    """Merge two nested dictionaries together"""
    for key in d2:
        if key in d1:
            if isinstance(d2[key], dict) and isinstance(d1[key], dict):
                merge_nested_dicts(d1[key], d2[key])
        else:
            d1[key] = d2[key]

    return d1

您可以通过更新结果嵌套的dict来合并:

nested_dict = {}

for key in d:
    paths = key.split('.')
    nested_path = make_nested_dict(paths, d[key])
    nested_dict = merge_nested_dicts(nested_dict, nested_path)

print(nested_dict)

现在,这给出了以下内容:

{'a': {'b': 'foo', 'c': 'bar'}, 'd': {'e': {'f': 'baz'}}}

完整的代码并附有评论:

def make_nested_dict(iterable, final):
    """Make nested dictionary path with a final attribute"""

    # If iterable, keep recursing
    if iterable:

        # Unpack key and rest of dict
        head, *tail = iterable

        # Return new dictionary, recursing on tail value
        return {head: make_nested_dict(tail, final)}

    # Otherwise assign final attribute
    else:
        return final

def merge_nested_dicts(d1, d2):
    """Merge two nested dictionaries together"""

    for key in d2:

        # If we have matching keys
        if key in d1:

            # Only merge if both values are dictionaries
            if isinstance(d2[key], dict) and isinstance(d1[key], dict):
                merge_nested_dicts(d1[key], d2[key])

        # Otherwise assign normally
        else:
            d1[key] = d2[key]

    return d1

if __name__ == "__main__":
    d = {"a.b": "foo", "a.c": "bar", "d.e.f":"baz", "d.e.g":"M", 'l':"single_key_test" }  

    nested_dict = {}

    for key in d:

        # Create path
        paths = key.split('.')

        # Create nested path
        nested_path = make_nested_dict(paths, d[key])

        # Update result dict by merging with new dict
        nested_dict = merge_nested_dicts(nested_dict, nested_path)

    print(nested_dict)