def unflatten(dictionary):
resultDict = dict()
for key, value in dictionary.items():
parts = key.split(".")
d = resultDict
for part in parts[:-1]:
if part not in d:
d[part] = dict()
d = d[part]
d[parts[-1]] = value
return resultDict
Answer from Messa on Stack Overflowdef unflatten(dictionary):
resultDict = dict()
for key, value in dictionary.items():
parts = key.split(".")
d = resultDict
for part in parts[:-1]:
if part not in d:
d[part] = dict()
d = d[part]
d[parts[-1]] = value
return resultDict
from collections import defaultdict
def unflatten(d):
ret = defaultdict(dict)
for k,v in d.items():
k1,delim,k2 = k.partition('.')
if delim:
ret[k1].update({k2:v})
else:
ret[k1] = v
return ret
Unflatten a Python dictionary - Stack Overflow
python - Make nested dictionary, unflatten - Stack Overflow
How would I go about unflattening a complex nested dict with lists?
python - Unflatten Dictionary Structure to Nested Dictionary - Stack Overflow
» pip install unflatten
You can do it with loop. Is it what you want following?
my_dict = {'435': ['188', '436', '439', '1038'], '188': ['198'], '436': ['86', '115', '437', '444', '626', '650'], '439': [], '1038': ['733', '1037'], '198': [], '86': [], '115': [], '437': [], '444': [], '626': [], '650': [], '733': ['14'], '1037': [], '14': []}
table = {}
roots = set(my_dict)
for name, children in my_dict.items():
table.setdefault(name, {'name': name})
if children:
table[name]['children'] = [
table.setdefault(child, {'name': child})
for child in children
]
roots -= set(children)
result = {k: v for k, v in table.items() if k in roots}
print(list(result.values()))
output:
[{'name': '435', 'children': [{'name': '188', 'children': [{'name': '198'}]}, {'name': '436', 'children': [{'name': '86'}, {'name': '115'}, {'name': '437'}, {'name': '444'}, {'name': '626'}, {'name': '650'}]}, {'name': '439'}, {'name': '1038', 'children': [{'name': '733', 'children': [{'name': '14'}]}, {'name': '1037'}]}]}]
explanation:
tableholds all items(name and children data).- If an item has
children, register them totablebydict.setdefault. rootscontains all items that doen't belong any other items.- At last, filter only items which are in
roots.
try using
# Python code to demonstrate
# converting multi-d list into 1d list
# using chain.from_iterables
# import chain
from itertools import chain
ini_list = {'435': ['188', '436', '439', '1038'], '188': ['198'], '436': ['86', '115', '437', '444', '626', '650'], '439': [], '1038': ['733', '1037'], '198': [], '86': [], '115': [], '437': [], '444': [], '626': [], '650': [], '733': ['14'], '1037': [], '14': []}
# printing initial list
for key in ini_list:
# converting 2d list into 1d
# using chain.from_iterables
flatten_list = list(chain.from_iterable(ini_list[key]))
# printing flatten_list
print ("final_result", str(flatten_list))
Extrapolated solution from here: Geeks for Geeks I'm sure this can be optimized further but this should work and essentially throw everything into a single list within each dict position print the result and then initialize with the next dict key-value.
Hope that helps. If so, please mark as answer. Thanks
Here is a solution using a tree of dict:
import collections
def tree():
return collections.defaultdict(tree)
def unflatten(pair_list):
root = tree()
for mount, path in pair_list:
parts = mount.split(":")
curr = root
for part in parts[:-1]:
index = int(part[1:-1]) if part[0] == "[" else part
curr = curr[index]
part = parts[-1]
index = int(part[1:-1]) if part[0] == "[" else part
curr[index] = path
return root
With the following input:
pair_list = [
['mounts:device', '/dev/sda3'],
['mounts:fstype:[0]', 'ext1'],
['mounts:fstype:[1]', 'ext3'],
['mounts:test:lala:fstype:[0]:[0]', 'abc']
]
You'll get:
{
"mounts": {
"fstype": {
"0": "ext1",
"1": "ext3"
},
"test": {
"lala": {
"fstype": {
"0": {
"0": "abc"
}
}
}
},
"device": "/dev/sda3"
}
}
Then you can use the recursive function make_listbellow to turn the integer indexes in a list.
def make_list(root):
if isinstance(root, str):
return root
keys = list(root.keys())
if all(isinstance(k, int) for k in keys):
values = [None] * (max(keys) + 1)
for k in keys:
values[k] = make_list(root[k])
return values
else:
return {k: make_list(v) for k, v in root.items()}
Here is the result with the pair_list:
flat = unflatten(pair_list)
flat = make_list(flat)
You'll get:
{'mounts': {'device': '/dev/sda3',
'fstype': ['ext1', 'ext3'],
'test': {'lala': {'fstype': [['abc']]}}}}
Is it fine?
input1=[
['mounts:device', '/dev/sda3'],
['mounts:fstype:[0]', 'ext1'],
['mounts:fstype:[1]', 'ext3']
]
input2={x[1]:x[0].split(':')[1] for x in input1}
input3=['ext3', 'ext1', '/dev/sda3']
input4=['fstype', 'fstype', 'device']
res={}
for x,y in zip(input3, input4):
res.setdefault(y,[]).append(x)
res1=res.keys()
res2=res.values()
res3=[x[0] for x in res2 if len(x)==1]+[x for x in res2 if len(x)>1]
result=dict(zip(res1,res3))
print result
Output :
{'device': '/dev/sda3', 'fstype': ['ext3', 'ext1']}
I need to flatten and unflatten a dict. I've figured out how to flatten it. As the dict is complex, this would be a representation of it:
di = {'a': [{'b': 'c', 'd': 'e'}, {'f': 'g'}, {'h': ['i']}]}Flattening it gives me this:
flat_di = {'a.0.b': 'c', 'a.0.d': 'e', 'a.1.f': 'g', 'a.2.h.0': 'i'}I've been trying to unflatten this for a while but my brain breaks when I try to figure it out. I know it's some form of recursion.
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}}}
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