Преобразование списка с иерархией во вложенный словарь с аналогичной иерархией с помощью 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']]

2 ответа

Я уверен, что есть способы получше, но вы могли бы написать рекурсивную функцию, которая заполняет заданный 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)

я думаю что setdefault() проходит долгий путь к решению этой проблемы, хотя вы также можете использовать вложенный collections.defaultdict(). Я думаю, что единственная проблема заключается в том, что в последнем листе вам нужна пустая строка, а не пустой dict, иначе это было бы очень просто.

      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': ''}}
Другие вопросы по тегам