I'm trying to convert dicts and lists into a structure for a Tree view component (Ignition Perspective component, if it's relevant) which requires it in a specific format, but I'm starting to feel stupid as I just can't get it right.
I need to convert this:
{
'barrelIds': ['BOB', 'SARAH'],
'executionMode': 'EMPTY_FILL',
'emptyRequest': {
'barrelGroupId': 123458,
'requestId': 7832
},
'complex': {
'sub-complex': ['BOB', 'SARAH'],
'sub-complex2': {
'barrelGroupId': 123458,
'barrels': [{
'item': 1
}, {
'item': 2
}
]
}
}
}
into this:
[
{
"label": "barrelIds",
"expanded": True,
"items": [
{
"label": "BOB",
"expanded": True,
"items": []
},
{
"label": "SARAH",
"expanded": True,
"items": []
}
]
},
{
"label": "executionMode = 'EMPTY_FILL'",
"expanded": True,
"items": []
},
{
"label": "emptyRequest",
"expanded": True,
"items": [
{
"label": "barrelGroupId = 123458",
"expanded": True,
"items": []
},
{
"label": "requestId = 7832",
"expanded": True,
"items": []
}
]
},
{
"label": "complex",
"expanded": True,
"items": [
{
"label": "sub-complex",
"expanded": True,
"items": [
{
"label": "BOB",
"expanded": False,
"items": []
},
{
"label": "SARAH",
"expanded": False,
"items": []
}
]
},
{
"label": "sub-complex2",
"expanded": True,
"items": [
{
"label": "barrelGroupId = 12358",
"expanded": True,
"items": []
},
{
"label": "barrels",
"expanded": True,
"items": [
{
"label": "[0]",
"expanded": True,
"items": [
{
"label": "item = 1",
"expanded": False,
"items": []
},
{
"label": "other = 1",
"expanded": False,
"items": []
}
]
},
{
"label": "[1]",
"expanded": True,
"items": [
{
"label": "item = 2",
"expanded": True,
"items": []
}
]
}
]
}
]
}
]
}
]
What this looks like in the Tree is this:
Essentially, the basic rules are:
{'label': '', 'items': [], 'expanded': True}
{'label': '{} = {}'.format(key, repr(value)), 'items': [], 'expanded': True}
{'label': key, 'items': [{'label'...}, {'label'...}], 'expanded': True}
I will include code, but I'm sure it should be rewritten in its entirety...
def get_tree_items(data):
# TODO:
# 1. simplify this. top conditions shouldn't have sub-fors.
# 2. empty dicts are not handled.
if isinstance(data, dict):
items = []
for key, value in data.items():
if isinstance(value, list):
sub_items = get_tree_items(value)
items.append(format_value(key, value, sub_items))
elif isinstance(value, dict):
sub_items = get_tree_items(value)
items.append(format_value(key, value, sub_items))
else:
items.append({'label': '{} = {}'.format(key, repr(value)), 'items': [],
'expanded': True})
return items
elif isinstance(data, list):
items = []
for idx, item in enumerate(data):
new_item = get_tree_items(item)
items.append(new_item)
return items
else:
return {'label': data, 'items': [], 'expanded': True}
def format_value(key, value, sub_items):
if isinstance(sub_items, list):
return {'label': key, 'items': sub_items, 'expanded': True}
else:
return {'label': '{} = {}'.format(key, value), 'items': [], 'expanded': True}
This currently produces this output:
[{
'label': 'barrelIds',
'items': [{
'label': 'BOB',
'items': [],
'expanded': True
}, {
'label': 'SARAH',
'items': [],
'expanded': True
}
],
'expanded': True
}, {
'label': "executionMode = 'EMPTY_FILL'",
'items': [],
'expanded': True
}, {
'label': 'emptyRequest',
'items': [{
'label': 'barrelGroupId = 123458',
'items': [],
'expanded': True
}, {
'label': 'requestId = 7832',
'items': [],
'expanded': True
}
],
'expanded': True
}, {
'label': 'complex',
'items': [{
'label': 'sub-complex',
'items': [{
'label': 'BOB',
'items': [],
'expanded': True
}, {
'label': 'SARAH',
'items': [],
'expanded': True
}
],
'expanded': True
}, {
'label': 'sub-complex2',
'items': [{
'label': 'barrelGroupId = 123458',
'items': [],
'expanded': True
}, {
'label': 'barrels',
'items': [[{
'label': 'item = 1',
'items': [],
'expanded': True
}
], [{
'label': 'item = 2',
'items': [],
'expanded': True
}
]],
'expanded': True
}
],
'expanded': True
}
],
'expanded': True
}
]
and this Tree view (note missing sub-complex2.barrels tree branches - everything else is correct):
I got it, finally! I rewrote the function from the ground up.
def process(obj, key_=None, idx_=None):
this_fn = process
if isinstance(obj, dict):
items = []
for key, value in obj.items():
items.append(this_fn(value, key_=key))
item = None
if key_ is not None:
item = {'label': key_, 'items': items, 'expanded': True}
elif idx_ is not None:
item = {'label': '[{}]'.format(idx_), 'items': items, 'expanded': True}
else:
item = items
return item
if isinstance(obj, list):
items = []
for idx, value in enumerate(obj):
items.append(this_fn(value, idx_=idx))
item = None
if key_ is not None:
item = {'label': key_, 'items': items, 'expanded': True}
elif idx_ is not None:
item = {'label': '[{}]'.format(idx_), 'items': items, 'expanded': True}
else: # is this even possible to get to??
raise ValueError('How did you get here??')
item = {'label': '???', 'items': items, 'expanded': True}
return item
else: # basic type, int, long, str, etc.
if idx_ is not None:
item = {'label': '[{}] = {}'.format(idx_, repr(obj)), 'items': [], 'expanded': True}
elif key_ is not None:
item = {'label': '{} = {}'.format(key_, repr(obj)), 'items': [], 'expanded': True}
else: # this should NEVER be an option...
raise ValueError('What?')
return item