Home > Software engineering >  Unflatten Dictionary Structure to Nested Dictionary
Unflatten Dictionary Structure to Nested Dictionary

Time:12-09

I'm trying to unflatten a list of dictionary items to a nested dictionary structure which should contain the following structure

Bus
   Message
          Fieldname

I currently have a way to get the messages inside the bus but this will only result in a single message being added to each bus because I end up overwriting the dictionary within the bus with the new message. Actual output below

{'BUS_A': {'MSG_1': None}, 'BUS_B': {'MSG_2': None}}

How can I create the below desired input from the input data including the bus items, messages and fields?

A complete, executable example of where I've currently got to so far.

signals = [{"bus":"BUS_A", "message":"MSG_1", "fieldname":"Signal1"},
           {"bus":"BUS_A", "message":"MSG_2", "fieldname":"Signal2"},
           {"bus":"BUS_B", "message":"MSG_3", "fieldname":"Signal3"},
           {"bus":"BUS_B", "message":"MSG_4", "fieldname":"Signal4"}]

desired_output = {"BUS_A":
                      {"MSG_1":
                           {"Signal1":None},
                       "MSG_2":
                            {"Signal2":None},
                       },
                  "BUS_B":
                      {"MSG_3":
                           {"Signal3":None},
                       "MSG_4":
                            {"Signal4":None},
                       }
                  }



def Unflatten(signals):
    buses = {item['bus']:{} for item in signals}
    for bus in buses:
        for item in signals:
            if item['bus'] == bus:
                buses[bus] = {item['message']:None}
    print(buses)

Unflatten(signals)

CodePudding user response:

Using a defaultdict saves you a lot of hassle having to conditionally set up the structure of the dictionary:

>>> signals = [{"bus":"BUS_A", "message":"MSG_1", "fieldname":"Signal1"},
...            {"bus":"BUS_A", "message":"MSG_1", "fieldname":"Signal2"},
...            {"bus":"BUS_B", "message":"MSG_2", "fieldname":"Signal3"},
...            {"bus":"BUS_B", "message":"MSG_2", "fieldname":"Signal4"}]
>>> from collections import defaultdict
>>> buses = defaultdict(lambda: defaultdict(dict))
>>> for s in signals:
...     buses[s["bus"]][s["message"]][s["fieldname"]] = None
...
>>> buses
defaultdict(<function <lambda> at 0x00000265E25451F0>, {'BUS_A': defaultdict(<class 'dict'>, {'MSG_1': {'Signal1': None, 'Signal2': None}}), 'BUS_B': defaultdict(<class 'dict'>, {'MSG_2': {'Signal3': None, 'Signal4': None}})})

Without defaultdict you need to initialize each value to an empty dictionary as needed:

>>> buses = {}
>>> buses = {}
>>> for s in signals:
...     if s["bus"] not in buses:
...         buses[s["bus"]] = {}
...     if s["message"] not in buses[s["bus"]]:
...         buses[s["bus"]][s["message"]] = {}
...     buses[s["bus"]][s["message"]][s["fieldname"]] = None
...
>>> buses
{'BUS_A': {'MSG_1': {'Signal1': None, 'Signal2': None}}, 'BUS_B': {'MSG_2': {'Signal3': None, 'Signal4': None}}}

CodePudding user response:

Here is one approach. It slims down some of the property juggling by starting with an object that already has 2 empty busses. This method is probably inferior to defaultdict, but it illustrates the logic behind unflattening your signals more directly. I can't pretend to know what you are doing, but this all seems like a bad way to do it, and to some degree pointless. What is the real outcome of this method ~ you assigned None to a signal name. All the data is already there, changing it's form is probably entirely unnecessary. Your 2 Busses should probably just be separate instantiations of a Bus class which has methods that can parse and organize the data that is fed to it. If you use a dataclass you could also use asdict (when/if necessary) to pass it's properties to whatever is expecting the data as a dict.

def unflatten(signals) -> dict:
    busses = {'BUS_A':dict(), 'BUS_B':dict()}
    for obj in signals:
        bus, msg, sig = obj['bus'], obj['message'], obj['fieldname']
        
        if not msg in busses[bus]:
            busses[bus][msg] = dict()
        
        busses[bus][msg][sig] = None
            
    return busses
  • Related