Giving the following list:
mapping_list = ['location/name', 'location/address/address1', 'location/address/zip', 'location/business/business_name', 'occupant/occupant_type']
How to turn it into a nested dictionary as following where the last value is the last key with an empty string as a default value.
{
"location":
{
"name": "",
"address":
{
"address1": "",
"zip": ""
},
"business":
{
"business_name": ""
}
},
"occupant":
{
"occupant_type": ""
}
}
Note : the given list could be written as such:
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']]
CodePudding user response:
I'm sure there are better ways but you could write a recursive function that populates a given dict
with a mapping such as you have.
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)
Alternative method utilizing 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)
CodePudding user response:
I think that setdefault()
goes a long way to solving this though you might also use a nested collections.defaultdict()
. I think the only wrinkle is that in the final leaf you want an empty string rather than an empty dict, otherwise it would be dead simple.
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)
This will give you:
{'location': {'name': '', 'address': {'address1': '', 'zip': ''}, 'business': {'business_name': ''}}, 'occupant': {'occupant_type': ''}}