Python: 合并嵌套字典的列表并得到一个字典(树)

Python: Merge list of nested dictionary and get a dictionary (tree)

例如,我有以下“dict”列表:

List1= [{'Europe': {'DE': {'Berlin': ['jack']}}},
        {'Europe': {'DE': {'KL': ['Paul']}}},
        {'Europe': {'FR': {'Paris': ['Jean', "Pierre"]}}}]

我想用 Python 将其转换为具有树结构的字典,如:

output = {"Europe": { "DE": { "KL":     ["Paul"],
                              "Berlin": ["Jack"]
                             },
                       "FR" : { "Paris": ["Jean", "Pierre"]}
                     }
          }

你能帮帮我吗?我的函数获取 list1 和 return 输出字典 ?
谢谢

这可行,尽管我觉得可能有更优雅的方法。

all_country_codes = set([list(x['Europe'].keys())[0] for x in List1])
output = []
for code in all_country_codes:
    results = [x['Europe'][code] for x in List1 if code in x['Europe'].keys()]
    country_code_dict = {}
    for country_dictionary in results:
        country_code_dict.update(country_dictionary)
    output.append({code: country_code_dict})

almost_there_dict = {}
for reformatted_dict in output:
    almost_there_dict.update(reformatted_dict)

final_dict = {}
final_dict['Europe'] = almost_there_dict

这是一个简单的递归解决方案。此解决方案足够通用,能够支持字典结构中的任何未来更改,您甚至可以将其用于此国家/地区列表以外的目的。通过利用 dictlist 的可变性 属性,我们可以在每个递归调用中传递它并执行:

  1. 如果lhs(左边,这里是List1)中的元素还没有出现在rhs(右边,这里是result), 按原样复制。
  2. 如果lhs中的元素已经出现在rhs中,则递归调用合并内部元素。
  3. 如果 lhs 中的值是一个列表,将其添加到 rhs
import copy
import json

List1= [
    {'Europe': {'DE': {'Berlin': ['Jack']}}},
    {'Europe': {'DE': {'KL': ['Paul']}}},
    {'Asia': {'PH': {'Manila': ['Jose', 'Rizal']}}},
    {'Europe': {'FR': {'Paris': ['Jean', "Pierre"]}}},
    {'Asia': {'PH': {'Manila': ['Andres']}}},
    {'Asia': {'KH': {'Siem Reap': ['Angkor']}}},
    {'Europe': {'DE': {'Berlin': ['Jill']}}},
    {'Asia': {'PH': {'Cebu': ['Lapulapu']}}},
    {'Asia': {'PH': {'Manila': ['Bonifacio']}}},
    {'Europe': {'ES': {'Valencia': ['Paella']}}},
    {'Asia': {'KH': {'Phnom Penh': ['Wat']}}},
    {'Europe': {'ES': {'Valencia': ['Me gusta mucho!']}}},
    {'Asia': {'PH': {'Palawan': ['Beach']}}},
    {'Asia': {'PH': {'Palawan': ['Cave']}}},
    {'Asia': {'PH': {'Palawan': []}}},
]

result = {}

def merge(lhs, rhs):
    if isinstance(lhs, dict):
        for key, value in lhs.items():
            if key not in rhs:
                rhs[key] = copy.deepcopy(value)  # Thanks to @sabik for the code review (see comments section). To avoid updating the source data List1, here we would perform a deep copy instead of just <rhs[key] = value>.
            else:
                merge(value, rhs[key])
    elif isinstance(lhs, list):
        rhs.extend(lhs)

for item in List1:
    merge(item, result)

print(json.dumps(result, indent=4))

输出:

{
    "Europe": {
        "DE": {
            "Berlin": [
                "Jack",
                "Jill"
            ],
            "KL": [
                "Paul"
            ]
        },
        "FR": {
            "Paris": [
                "Jean",
                "Pierre"
            ]
        },
        "ES": {
            "Valencia": [
                "Paella",
                "Me gusta mucho!"
            ]
        }
    },
    "Asia": {
        "PH": {
            "Manila": [
                "Jose",
                "Rizal",
                "Andres",
                "Bonifacio"
            ],
            "Cebu": [
                "Lapulapu"
            ],
            "Palawan": [
                "Beach",
                "Cave"
            ]
        },
        "KH": {
            "Siem Reap": [
                "Angkor"
            ],
            "Phnom Penh": [
                "Wat"
            ]
        }
    }
}

我认为你可以使用嵌套的 defaultdict 来完成它。这是解决方案,它的灵感来自 Nested defaultdict of defaultdict

import json
from collections import defaultdict

# List1 = [{'Europe': {'DE': {'Berlin': ['jack']}}},
#      {'Europe': {'DE': {'KL': ['Paul']}}},
#      {'Europe': {'FR': {'Paris': ['Jean', "Pierre"]}}}]

List1 = [{'Europe': {'DE': {'Berlin': {'a': ['jack']}}}},
         {'Europe': {'DE': {'Berlin': {'b': {'b1': ['xxx']}}}}}, 
         {'Europe': {'FR': {'Paris': ['Jean', "Pierre"]}}}]

def create_tree():
    return defaultdict(create_tree)

def set_node(i, node, k):
    if isinstance(i, dict):
        for k, v in i.items():
            if isinstance(v, list):
                node[k] = v
            else:
                node = node[k]
                set_node(v, node, k)
                                
def get_tree():
    tree = create_tree()
    for d in List1:
        set_node(d, tree, None)
    return tree

tree = get_tree()
# if you want to get the dict of tree, use:json.loads(json.dumps(tree))
print(json.dumps(tree, indent=4))

如果嵌套的深度 dict 没有改变,您可以尝试循环并在找到键时使用 setdefaultupdate:-

List1= [{'Europe': {'DE': {'Berlin': ['Jack']}}},
        {'Europe': {'DE': {'KL': ['Paul']}}},
        {'Europe': {'FR': {'Paris': ['Jean', "Pierre"]}}}]

output = {}
for l in List1:
    
    for k,v in l.items():
        #outer key
        output.setdefault(k, {})
        
        for key,val in v.items():
            #inner key
            output[k].setdefault(key, {})
            #update inner key
            output[k][key].update(val)
            

哪个 returns(请记住,根据您的 Python 版本,dict 可能排序或不排序):

{'Europe': {'DE': {'Berlin': ['Jack'], 'KL': ['Paul']},
  'FR': {'Paris': ['Jean', 'Pierre']}}}