As a simplified example, suppose I have the following dictionary that tells me the number of employees in different locations of a company:
start_dict = {
"/BUILDING_A/FLOOR_3/ROOM_12": 3,
"/BUILDING_A/FLOOR_3/ROOM_15": 4,
"/BUILDING_A/FLOOR_4/ROOM_3": 2,
"/BUILDING_B/FLOOR_1": 5,
"/BUILDING_B/FLOOR_2": 3,
"/BUILDING_C": 25,
"/BUILDING_D": 32,
}
Each dictionary key is a string with one or more slash-separated elements. In my real data each key can contain an arbitrary amount of elements.
I'm looking to convert this start dictionary into a nested dictionary. The desired result would look as follows:
{
"BUILDING_A": {
"FLOOR_3": {
"ROOM_12": 3,
"ROOM_15": 4,
},
"FLOOR_4": {
"ROOM_3": 2
},
},
"BUILDING_B": {
"FLOOR_1": 5,
"FLOOR_2": 3,
},
"BUILDING_C": 25,
"BUIDLING_D": 32,
}
Tried thus far
I have tried to adapt this answer, but can't quite get it to work. This is the code that I came up with:
result_dict = {}
for key, value in start_dict.items():
new_keys = key.lstrip("/").split("/")
current_dict = result_dict
for new_key in new_keys[:-1]:
current_dict = current_dict.get(new_key, {})
current_dict[new_keys[-1]] = value
which gives the following as result_dict
:
{'BUILDING_C': 25, 'BUILDING_D': 32}
CodePudding user response:
You want to insert the new_key
here, if it is not yet contained in the dict:
current_dict = current_dict.get(new_key, {})
Do so by changing that line to
current_dict = current_dict.setdefault(new_key, {})
Result:
{
"BUILDING_A": {
"FLOOR_3": {
"ROOM_12": 3,
"ROOM_15": 4
},
"FLOOR_4": {
"ROOM_3": 2
}
},
"BUILDING_B": {
"FLOOR_1": 5,
"FLOOR_2": 3
},
"BUILDING_C": 25,
"BUILDING_D": 32
}
CodePudding user response:
Try:
start_dict = {
"/BUILDING_A/FLOOR_3/ROOM_12": 3,
"/BUILDING_A/FLOOR_3/ROOM_15": 4,
"/BUILDING_A/FLOOR_4/ROOM_3": 2,
"/BUILDING_B/FLOOR_1": 5,
"/BUILDING_B/FLOOR_2": 3,
"/BUILDING_C": 25,
"/BUILDING_D": 32,
}
def to_dict(k, v):
keys = k.strip("/").split("/", maxsplit=1)
if len(keys) == 1:
return {keys[0]: v}
return {keys[0]: to_dict(keys[1], v)}
# merge to d1
def merge(d1, d2):
for k, v in d2.items():
if k in d1:
merge(d1[k], v)
else:
d1[k] = v
out = {}
for k, v in start_dict.items():
merge(out, to_dict(k, v))
print(out)
Prints:
{
"BUILDING_A": {
"FLOOR_3": {"ROOM_12": 3, "ROOM_15": 4},
"FLOOR_4": {"ROOM_3": 2},
},
"BUILDING_B": {"FLOOR_1": 5, "FLOOR_2": 3},
"BUILDING_C": 25,
"BUILDING_D": 32,
}
CodePudding user response:
The issue with your code is current_dict = current_dict.get(new_key, {})
line. If the key does not exists, you create new object, but you don't assign it to original object.
So the working code would be:
result_dict = {}
for key, value in start_dict.items():
new_keys = key.lstrip("/").split("/")
current_dict = result_dict
for new_key in new_keys[:-1]:
if new_key not in current_dict:
current_dict[new_key] = {}
current_dict = current_dict[new_key]
current_dict[new_keys[-1]] = value
The full demo (be patient if when you hit Run the snippet
:
<script defer src="https://pyscript.net/unstable/pyscript.js"></script>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<div id="out"></div>
<py-script output="out" style="display: none">
from pprint import pprint
start_dict = {
"/BUILDING_A/FLOOR_3/ROOM_12": 3,
"/BUILDING_A/FLOOR_3/ROOM_15": 4,
"/BUILDING_A/FLOOR_4/ROOM_3": 2,
"/BUILDING_B/FLOOR_1": 5,
"/BUILDING_B/FLOOR_2": 3,
"/BUILDING_C": 25,
"/BUILDING_D": 32,
}
result_dict = {}
for key, value in start_dict.items():
new_keys = key.lstrip("/").split("/")
current_dict = result_dict
for new_key in new_keys[:-1]:
if new_key not in current_dict:
current_dict[new_key] = {}
current_dict = current_dict[new_key]
current_dict[new_keys[-1]] = value
pprint(result_dict)
</py-script>