I have dictionary like this, need to fill it into tree like scheme in array or DB for example:
a = {"seasons": "episodes", "peka": {"lol": "wow", "kek": {"wtf": "is this"}}, "ololo": "wololo"}
key "seasons" have own ID = 1, Parent_ID = NONE
and value "episode" have its own ID = 2 and Parent_ID = 1 ,
and the same with other item of dictionary.
/!\ WARNING the order in a dictionnary is not guaranteed see here
a = {
"seasons": "episodes",
"peka": {"lol": "wow", "kek": {"wtf": "is this"}},
"ololo": "wololo"
}
The object a is dictionnary the order of (key, value) is not guaranteed, that is to say random if you do print(a), you have :
{'ololo': 'wololo', 'peka': {'kek': {'wtf': 'is this'}, 'lol': 'wow'}, 'seasons': 'episodes'}
It is another order.
To keep the same order, copy/past this type in the file file.json and user
OrderedDict.
file.json:
{
"seasons": "episodes",
"peka": {"lol": "wow", "kek": {"wtf": "is this"}},
"ololo": "wololo"
}
Here your solution:
import json
from collections import OrderedDict
from pprint import pprint
with open('file.json', 'r') as filename:
a = json.load(filename, object_pairs_hook=OrderedDict)
def build_item(_id, parent_id, value):
return {'ID': _id, 'Parent_ID': parent_id, 'Value': value}
def dfs(_id, root, tree):
_id += 1
flat_tree = [build_item(_id, None, root)]
stack = [(_id, tree)]
while len(stack) != 0:
parent_id, tree = stack.pop(0)
if isinstance(tree, dict):
for value in tree.keys():
_id += 1
flat_tree.append(build_item(_id, parent_id, value))
stack.append((_id, tree[value]))
else:
value = tree
_id += 1
flat_tree.append(build_item(_id, parent_id, value))
return _id, flat_tree
def convert_dict_to_flat_tree(d):
flat_trees = list()
_id = 0
for root, tree in d.items():
_id, flat_tree = dfs(_id, root, tree)
flat_trees.extend(flat_tree)
return flat_trees
flat_tree = convert_dict_to_flat_tree(a)
pprint(flat_tree)
Output:
[{'ID': 1, 'Parent_ID': None, 'Value': 'seasons'},
{'ID': 2, 'Parent_ID': 1, 'Value': 'episodes'},
{'ID': 3, 'Parent_ID': None, 'Value': 'peka'},
{'ID': 4, 'Parent_ID': 3, 'Value': 'lol'},
{'ID': 5, 'Parent_ID': 3, 'Value': 'kek'},
{'ID': 6, 'Parent_ID': 4, 'Value': 'wow'},
{'ID': 7, 'Parent_ID': 5, 'Value': 'wtf'},
{'ID': 8, 'Parent_ID': 7, 'Value': 'is this'},
{'ID': 9, 'Parent_ID': None, 'Value': 'ololo'},
{'ID': 10, 'Parent_ID': 9, 'Value': 'wololo'}]
You want something like this:
a = {"seasons": "episodes", "peka": {"lol": "wow", "kek": {"wtf": "is this"}}, "ololo": "wololo"}
_id = {}
def newid():
id = _id.setdefault('foo', 0)
_id['foo'] += 1
return id
def flat(dic, parent):
for k,v in dic.items():
id = newid()
yield (id, parent, k, v if not isinstance(v, dict) else None)
if isinstance(v, dict):
for tup in flat(v, id):
yield tup
print list(flat(a, newid()))
Which prints:
[(1, 0, 'seasons', 'episodes'),
(2, 0, 'ololo', 'wololo'),
(3, 0, 'peka', None),
(4, 3, 'kek', None),
(5, 4, 'wtf', 'is this'),
(6, 3, 'lol', 'wow')]
These are tuples in the form (ID, Parent ID, Key, Value?). I would have prefer to output E(ID, Parent ID, Key) V(ID, Value).
Related
So I have this params:
p = [{'quantity': 1}, {'args': {'id': 12345678, 'age': 12}}]
And I want to be able to search for quantity and get the value 1 or the args key and get its doctionary ({'id': 12345678, 'age: 12})
This is what I have try:
def search(name: str, params: list[dict]):
try:
return next((x[name] for x in params if x), None)
except KeyError:
return None
I case I search for the value of quantity:
search(name='quantity', params=p)
This return 1
But in case I want to value of args:
search(name='args', params=p)
This return None
I have a set of functions that you could use for this [ getNestedVal(p, 'args') would return {'id': 12345678, 'age: 12} ]...or you can use this generator
def yieldNestedVals(obj, key):
if isinstance(obj, str): return
try: yield obj[key]
except: pass
if isinstance(obj, dict): obj = obj.values()
for i in (obj if hasattr(obj, '__iter__') else []):
for v in yieldNestedVals(i, key): yield v
inside search like
def search(name, params, defaultVal=None):
for v in yieldNestedVals(params, name): return v # return 1st generated value
return defaultVal # if nothing if generated
[ Ofc you can omit both the defaultVal parameter and the last line if you want to just return None when nothing if found, since that is the default behavior when a function doesn't come across a return statement. ]
Now search('args', p) should also return {'id': 12345678, 'age: 12} and you can try it with other keys as well:
# p = [{'quantity': 1}, {'args': {'id': 12345678, 'age': 12}}]
{k: search(k, p) for k in ['quantity', 'args', 'id', 'age', 'dneKey']}
would return
{'quantity': 1,
'args': {'id': 12345678, 'age': 12},
'id': 12345678,
'age': 12,
'dneKey': None}
I'm trying to make a function that would take nested array (dict/list in any order) and a key name as arguments and return all values of that key in a list.
my_key = "Items"
my_dict = [{'z': 0, 'x': 0, 'y': 0, 'Items': [{'Slot': 1, 'id': 'minecraft:rail', 'Count': 1}, {'Slot': 2, 'id': 'minecraft:white_shulker_box', 'tag': {'BlockEntityTag': {'id': 'minecraft:shulker_box', 'Items': [{'Slot': 0, 'Count': 1, 'tag': {'Items': [{'id': 'minecraft:amethyst_shard', 'Count': 1}]}, 'id': 'minecraft:bundle'}]}}, 'Count': 1}]}]
def recursive_lookup(data, key):
if isinstance(data, list):
for i in data:
recursive_lookup(i, key)
elif isinstance(data, dict):
for i, v in data.items():
if i == key:
print(f'{v = }')
if isinstance(v, list) or isinstance(v, dict): recursive_lookup(v, key)
print(recursive_lookup(my_dict, my_key))
Currently it prints out found items at print(f'{v = }'). How can I store those in a list and pass as a function return?
You can use .extend() to concatenate the result of recursive calls to a list.
def recursive_lookup(data, key):
values = []
if isinstance(data, list):
for i in data:
values.extend(recursive_lookup(i, key))
elif isinstance(data, dict):
for i, v in data.items():
if i == key:
values.append(v)
if isinstance(v, list) or isinstance(v, dict):
values.extend(recursive_lookup(v, key))
return values
You can what you want without any explicit recursion at all by making use of the json module in the standard library (assuming your data can be serialized into that format). This is because the JSON decoder supports an object_hook argument which is a function it will call everytime it encounters a dictionary.
The basic idea is to specify a function via this argument that merely "watches" what is being decoded and checks it for the sought-after key.
Here's what I mean:
import json
my_key = "Items"
my_dict = [{'z': 0, 'x': 0, 'y': 0, 'Items': [{'Slot': 1, 'id': 'minecraft:rail', 'Count': 1}, {'Slot': 2, 'id': 'minecraft:white_shulker_box', 'tag': {'BlockEntityTag': {'id': 'minecraft:shulker_box', 'Items': [{'Slot': 0, 'Count': 1, 'tag': {'Items': [{'id': 'minecraft:amethyst_shard', 'Count': 1}]}, 'id': 'minecraft:bundle'}]}}, 'Count': 1}]}]
def lookup(data, key):
results = []
def decode_dict(a_dict):
try:
results.append(a_dict[key])
except KeyError:
pass
return a_dict
json_repr = json.dumps(data) # Convert to JSON format.
json.loads(json_repr, object_hook=decode_dict) # Return value ignored.
return results
from pprint import pprint
pprint(lookup(my_dict, my_key), sort_dicts=False)
Pretty-printed result list:
[[{'id': 'minecraft:amethyst_shard', 'Count': 1}],
[{'Slot': 0,
'Count': 1,
'tag': {'Items': [{'id': 'minecraft:amethyst_shard', 'Count': 1}]},
'id': 'minecraft:bundle'}],
[{'Slot': 1, 'id': 'minecraft:rail', 'Count': 1},
{'Slot': 2,
'id': 'minecraft:white_shulker_box',
'tag': {'BlockEntityTag': {'id': 'minecraft:shulker_box',
'Items': [{'Slot': 0,
'Count': 1,
'tag': {'Items': [{'id': 'minecraft:amethyst_shard',
'Count': 1}]},
'id': 'minecraft:bundle'}]}},
'Count': 1}]]
You can keep a running list:
def recursive_lookup(data, key):
lst = []
if isinstance(data, list):
for i in data:
lst.append(recursive_lookup(i, key))
elif isinstance(data, dict):
for i, v in data.items():
if i == key:
lst.append([v])
if isinstance(v, list) or isinstance(v, dict): lst.append(recursive_lookup(v, key))
return lst
print(recursive_lookup(data, 'Items'))
I wanna make a dictionary has name's key & data.In views.py I wrote
data_dict ={}
def try_to_int(arg):
try:
return int(arg)
except:
return arg
def main():
book4 = xlrd.open_workbook('./data/excel1.xlsx')
sheet4 = book4.sheet_by_index(0)
data_dict_origin = OrderedDict()
tag_list = sheet4.row_values(0)[1:]
for row_index in range(1, sheet4.nrows):
row = sheet4.row_values(row_index)[1:]
row = list(map(try_to_int, row))
data_dict_origin[row_index] = dict(zip(tag_list, row))
if data_dict_origin['name'] in data_dict:
data_dict[data_dict_origin['name']].update(data_dict_origin)
else:
data_dict[data_dict_origin['name']] = data_dict_origin
main()
When I printed out data_dict,it is
OrderedDict([(1, {'user_id': '100', 'group': 'A', 'name': 'Tom', 'dormitory': 'C'}), (2, {'user_id': '50', 'group': 'B', 'name': 'Blear', 'dormitory': 'E'})])
My ideal dictionary is
dicts = {
Tom: {
'user_id': '100',
'group': 'A',
'name': 'Tom',
'dormitory': 'C'
},
Blear: {
},
}
How should I fix this?What should I write it?
The code is using the wrong key in the dictionary. The keys are 1, 2, and do not have the name key. You can use this code instead:
for value in data_dict.values():
if value['name'] in data_dict:
data_dict[value['name']].update(value)
else:
data_dict[value['name']] = value
Your data_dict_origin has numbers as keys and dicts as values (which technically makes it a sparse array of dicts). The "name" key exists in those dicts, not in your data_dict.
Currently I have these input:
query = [{'id': 1, 'desc': 'desc_father', 'parent_id': None}
,{'id': 2, 'desc': 'desc_child_1', 'parent_id': 10}
,{'id': 3, 'desc': 'desc_child_2', 'parent_id': 2}
,{'id': 4, 'desc': 'desc_child_5', 'parent_id': 5}
,{'id': 5, 'desc': 'desc_child_6', 'parent_id': 6}
,{'id': 6, 'desc': 'desc_child_1', 'parent_id': 1}]
This is my recursive function:
def recursive(parent_list, child_dict, parent_id):
for l in parent_list:
if parent_id in l.values():
if 'children' not in l:
l['children'] = []
l['children'].append(child_dict)
break
else:
for i in l:
if isinstance(l[i], list):
recursive(d[i], child_dict, parent_id)
return parent_list
This is my main code:
results = []
for q in query:
dict_item = {}
dict_item['id'] = q['id']
dict_item['desc'] = q['desc']
if q['parent_id'] is None:
results.append(dict_item)
else:
results= recursive(results, dict_item, q['parent_id'])
return results
So, with above given data and the code, I have the result as below:
[{
'desc' : 'desc_father',
'id' : 1,
'children' : [{
'desc' : 'desc_child_1',
'id' : 2,
'children' : [{
'desc' : 'desc_child_2',
'id' : 3
}
]
}, {
'desc' : 'desc_child_1',
'id' : 6
}
]
}
]
This result is as you could see missing the items with id = 4 and id = 5 because during the loops, the parents of these items haven't been created yet (the items with id = 5 & id = 6). I am having difficulties in fixing this problem as I don't know how to traverse back or forward the list to create the father item before the children ones. Help is appreciated. Thanks in advance.
UPDATED
I have added in one case for my query, which is the item with id = 2, this time the item is updated its parent_id to 10 (parent_id = 10), since we do not have the item with id = 10 as parent in our return result, so this id = 2 item will also be a root.
My new code based on Scott Hunter guidance but I still could not make it to work. I must have misunderstood somewhere:
new_dict = {}
for q in query:
q['Children'] = []
new_dict[q['id']] = q
for k, v in new_dict.iteritems():
print k, v
if v['parent_id'] is not None and v['parent_id'] in new_dict:
new_dict[k]['Children'].append(v)
print new_dict
UPDATED-2
Now I make it to work, based on Scott Hunter suggestion, please see my below code. However the code looks ugly with too many for, is there anyway that I could perfect this? Thanks a lot for your support, just one more step and it will be done!
new_dict = {}
for q in query:
q['children'] = []
q['parent'] = 1
new_dict[q['id']] = q
for k, v in new_dict.iteritems():
p_id = v['parent_id']
for kk, vv in new_dict.iteritems():
if kk == p_id:
v['parent'] = 0
vv['children'].append(v)
results = []
for d_id, d_item in new_dict.iteritems():
if d_item['parent'] == 1:
results.append(d_item)
print results
This would be my solution:
#! /usr/bin/env python3
from pprint import pprint
query = [{'id': 1, 'desc': 'desc_father', 'parent_id': None}
,{'id': 2, 'desc': 'desc_child_1', 'parent_id': 1}
,{'id': 3, 'desc': 'desc_child_2', 'parent_id': 2}
,{'id': 4, 'desc': 'desc_child_5', 'parent_id': 5}
,{'id': 5, 'desc': 'desc_child_6', 'parent_id': 6}
,{'id': 6, 'desc': 'desc_child_1', 'parent_id': 1}]
def rec(query, parent):
parent['children'] = []
for item in query:
if item['parent_id'] == parent['id']:
parent['children'].append(item)
rec(query, item)
root = {'id': None}
rec(query, root)
pprint(root, indent=4)
It gives me the output (The keys are out of order, but that's what you get when you use a dictionary)
maurice#ubuntu:~/Dev/random$ python recursion_tree.py
{ 'children': [ { 'children': [ { 'children': [],
'desc': 'desc_child_2',
'id': 3,
'parent_id': 2}],
'desc': 'desc_child_1',
'id': 2,
'parent_id': 1},
{ 'children': [ { 'children': [ { 'children': [ ],
'desc': 'desc_child_5',
'id': 4,
'parent_id': 5}],
'desc': 'desc_child_6',
'id': 5,
'parent_id': 6}],
'desc': 'desc_child_1',
'id': 6,
'parent_id': 1}],
'desc': 'desc_father',
'id': 1,
'parent_id': None}
This should even work with multiple root nodes (there will be a dummy Node with the id None at the top though)
This does not require recursion.
First create a dictionary of nodes, one for each item using id as the key, which includes an empty list of children. Then you can scan that dictionary, and add each node to the list of children for its parent (skipping those whose parent is None). Once this scan is complete, every node that isn't a root will be in the child list of its parent, and thus all trees will be complete.
The roots of the forrest are the nodes that have None for a parent.
I have a dictionary that looks like this:
{'items': [{'id': 1}, {'id': 2}, {'id': 3}]}
and I'm looking for a way to directly get the inner dictionary with id = 1.
Is there a way to reach this other than looping the list items and comparing the id?
first_with_id_or_none = \
next((value for value in dictionary['items'] if value['id'] == 1), None)
You will have to loop through the list. The good news is is that you can use a generator expression with next() to do that looping:
yourdict = next(d for d in somedict['items'] if d['id'] == 1)
This can raise a StopIteration exception if there is no such matching dictionary.
Use
yourdict = next((d for d in somedict['items'] if d['id'] == 1), None)
to return a default instead for that edge-case (here None is used, but pick what you need).
Make it into a function:
def get_inner_dict_with_value(D, key, value):
for k, v in D.items():
for d in v:
if d.get(key) == value:
return d
else:
raise ValueError('the dictionary was not found')
With explanation:
def get_inner_dict_with_value(D, key, value):
for k, v in D.items(): # loop the dictionary
# k = 'items'
# v = [{'id': 1}, {'id': 2}, {'id': 3}]
for d in v: # gets each inner dictionary
if d.get(key) == value: # find what you look for
return d # return it
else: # none of the inner dictionaries had what you wanted
raise ValueError('the dictionary was not found') # oh no!
Running it:
>>> get_inner_dict_with_value({'items': [{'id': 1}, {'id': 2}, {'id': 3}]}, 'id', 1)
{'id': 1}
Another method:
def get_inner_dict_with_value2(D, key, value):
try:
return next((d for l in D.values() for d in l if d.get(key) == value))
except StopIteration:
raise ValueError('the dictionary was not found')
>>> get_inner_dict_with_value2({'items': [{'id': 1}, {'id': 2}, {'id': 3}]}, 'id', 1)
{'id': 1}