Home > Enterprise >  Dynamically create nested dictionary
Dynamically create nested dictionary

Time:08-15

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>

  • Related