Reputation: 2025
I have a hierarchy in a list like this. The real hierarchy is more complex, there may be an unlimited depth of nesting, its not just rooms in floors.
locations = [
{
"loc": "root",
"id": "floor_1",
"name": "floor_1 name"
},
{
"loc": "root",
"id": "floor_2",
"name": "floor_2 name"
},
{
"loc": "floor_1",
"id": "room_1-1",
"name": "room_1-1 name"
},
{
"loc": "floor_1",
"id": "room_1-2",
"name": "room_1-2 name"
},
{
"loc": "floor_2",
"id": "room_2-1",
"name": "room_2-1 name"
}
I need to convert it into a nested dictionary like this:
{
'floor_1': {
'name': 'floor_1 name',
'room_1-1': {
'name': 'room_1-1 name',
},
'room_1-2': {
'name': 'room_1-2 name',
}
},
'floor_2': {
'name': 'floor_2 name',
'room_2-1': {
'name': 'room_2-1 name',
}
}
}
I can't figure out how to put the 'name' leaf and the child-tree into the same dict. The closest I get is this:
def build(loc):
children = filter(lambda l: l['loc'] == loc, locations)
return {
child['id']: {
'name': child['name'],
'xxx': build(child['id'])
}
for child in children
}
build('root')
which obviously produces the wrong output:
{'floor_1': {'name': 'floor_1 name',
'xxx': {'room_1-1': {'name': 'room_1-1 name', 'xxx': {}},
...
I think I have to change the comprehension to something like this:
{ child['id']: build2(child['id']) for child in children }
but then I'm missing the leaf nodes ('name')
Upvotes: 1
Views: 1254
Reputation: 151
Not exactly what you want, but more generic. Hopefully useful to you.
import json
class TableDict(dict):
"""Table like data inside nested dictionary."""
def __init__(self, **kwargs):
super(TableDict, self).__init__({})
self.keyorder = kwargs['keyorder']
self.group_map = kwargs['group_map']
def import_dictlist(self, dlist):
"""Import iterable of dictionaries."""
for d in dlist:
ptr = None
for k in self.keyorder:
val = d.pop(k)
if ptr is None:
if val not in self:
self.update({val: {}})
ptr = self[val]
else:
if val not in ptr:
ptr.update({val: {}})
ptr = ptr[val]
self._get_dict_by_keys(ptr, d, k)
if bool(d) is True:
ptr.update({'values': d})
def _get_dict_by_keys(self, t_d, d, key):
"""Add group keys to nested dict."""
result = {}
if key in self.group_map:
for k in self.group_map[key]:
if k in d:
result.update({k: d.pop(k)})
if bool(result) is True:
#t_d.update({'values': result})
t_d.update(result)
if __name__ == '__main__':
locations = [{'id': 'floor_1', 'name': 'floor_1 name', 'loc': 'root'},
{'id': 'floor_2', 'name': 'floor_2 name', 'loc': 'root'},
{'id': 'room_1-1', 'name': 'room_1-1 name', 'loc': 'floor_1'},
{'id': 'room_1-2', 'name': 'room_1-2 name', 'loc': 'floor_1'},
{'id': 'room_2-1', 'name': 'room_2-1 name', 'loc': 'floor_2'}]
d = {'keyorder': ['loc', 'id'],
'group_map': {'id':['name']}}
td = TableDict(**d)
td.import_dictlist(locations)
print(json.dumps(td, indent =3))
Upvotes: 1
Reputation: 6914
Here is my attempt to rewrite your build
function:
def build(loc_key):
children = [(item['id'], item['name']) for item in locations if item['loc'] == loc_key]
data = {}
for key, name in children:
data[key] = {'name': name}
for item in locations:
if item['loc'] == key:
data[key].update(build(key))
return data
Upvotes: 1
Reputation: 402263
Hopefully this is what you want.
>>> layout = { i['id'] : {"name" : i['name']} for i in locations if 'floor' in i['id'] }
>>>
>>> for i in locations:
... if i['loc'] in layout:
... layout[i['loc']].update({i['id'] : { "name" : i['name']}})
...
>>> import json; print(json.dumps(layout, indent=2))
{
"floor_1": {
"room_1-2": {
"name": "room_1-2 name"
},
"name": "floor_1 name",
"room_1-1": {
"name": "room_1-1 name"
}
},
"floor_2": {
"room_2-1": {
"name": "room_2-1 name"
},
"name": "floor_2 name"
}
}
>>>
The order shown isn't the same but the fields are exactly as described in your example.
Upvotes: 1