使用 Python 将具有层次结构的列表转换为具有相似层次结构的嵌套字典

Convert list with hierarchy into nested dictionary with similar hierarchy using Python

给出以下列表:

mapping_list = ['location/name', 'location/address/address1', 'location/address/zip', 'location/business/business_name', 'occupant/occupant_type']

如何将其变成嵌套字典,如下所示,其中最后一个值是最后一个键,默认值为空字符串。

{
    "location":
    {
        "name": "",
        "address":
        {
            "address1": "",
            "zip": ""
        },
        "business":
        {
            "business_name": ""
        }
    },
    "occupant":
    {
        "occupant_type": ""
    }
}

注意:给定的列表可以这样写:

mapping_list_of_lists = []

for full_path in mapping_list:
  path_list = full_path.split('/')
  mapping_list_of_lists.append(path_list)

print(mapping_list_of_lists)


[['location', 'name'], ['location', 'address', 'address1'], ['location', 'address', 'zip'], ['location', 'business', 'business_name'], ['occupant', 'occupant_type']]

我相信有更好的方法,但您可以编写一个递归函数,用您拥有的映射填充给定的 dict

mapping_list = [
    'location/name', 
    'location/address/address1', 
    'location/address/zip', 
    'location/business/business_name', 
    'occupant/occupant_type'
]

def populate(mapping, dct, default=''):
    # check if '/' is in your current mapping
    if '/' in mapping:
        pos = mapping.find('/')
        part = mapping[:pos]
        # if it is and the next dict level does not exist yet
        # create the empty dict and recursively call the 
        # function to populate the inner parts with the
        # remainder of the mapping
        if part not in dct:
            dct[part] = dict()
        populate(mapping[pos+1:], dct[part], default=default)
    # otherwise, you're on your last part
    # and can safely fill the default value
    else:
        dct[mapping] = default

dct = {}
for mapping in mapping_list:
    populate(mapping, dct)
print(dct)

利用str.split('/')的替代方法:

mapping_list = [
    'location/name', 
    'location/address/address1', 
    'location/address/zip', 
    'location/business/business_name', 
    'occupant/occupant_type'
]

def populate(mapping, dct, default=''):
    if len(mapping) > 1:
        if mapping[0] not in dct:
            dct[mapping[0]] = dict()
        populate(mapping[1:], dct[mapping[0]], default=default)
    else:
        dct[mapping[0]] = default

mapping_list_of_lists = [i.split('/') for i in mapping_list]
dct = {}
for part in mapping_list_of_lists:
    populate(part, dct)
print(dct)

我认为 setdefault() 对解决这个问题有很大帮助,尽管您也可以使用嵌套的 collections.defaultdict()。我认为唯一的问题是在最后一片叶子中你想要一个空字符串而不是一个空字典,否则它会很简单。

mapping_list = [
    'location/name', 
    'location/address/address1', 
    'location/address/zip', 
    'location/business/business_name', 
    'occupant/occupant_type'
]

result = {}

for keys in [keys.split("/") for keys in mapping_list]:
    tmp = result
    for index, key in enumerate(keys):
        default = "" if index == (len(keys) -1) else {}
        tmp = tmp.setdefault(key, default)

print(result)

这会给你:

{'location': {'name': '', 'address': {'address1': '', 'zip': ''}, 'business': {'business_name': ''}}, 'occupant': {'occupant_type': ''}}