I have a nested dictionary as the following.
myTodo= {
'taskid': '10',
'taskstatus': 'in progress',
'Kitchen':
{
'Stove':{
'LED1':
{
'taskid': '11',
'taskstatus':'running'
},
'LED2':
{
'taskid': '12',
'taskstatus':'off',
'LEDSub':
{
'taskid': '13',
'taskstatus':'stable',
'LEDSub2':
{
'taskid': '14',
'taskstatus':'burnt'
}
}
},
'LED3':
{
'taskid': '15',
'taskstatus':'new'
}
},
//other nested layers
}
I have a python method which returns me the route based on keys to get to a 'value'. The method is shown below.
def route(myTodo, id):
q = list()
q.append((list(), myTodo))
while q:
this_key_chain, this_v = reverse_linked_q.pop()
# finish search if found the id
if this_v == id:
return this_key_chain
# not found. keep searching
try:
items = this_v.items()
except AttributeError:
continue
for k, v in items:
q.append((this_key_chain + [k], v))
raise KeyError
This method returns me the route in terms of the keys. So if I route(myTodo, "11")
, it will return me the list, keyroute=["Kitchen", "Stove", "LED1", "taskid"].
Then I delete the "taskid" from the keyroute list and send that list to the following method.
def createOutputDic(keyroute, myTodo):
for k in keyroute:
myTodo = myTodo.get(k)
return j
For route(myTodo, "11")
, the output from the createOutputDic(keyroute,myTodo)
is
{
'taskid': '11',
'taskstatus':'running'
}
The issue comes when I make the call as route(myTodo, "12")
. I expect to get
{ 'taskid': '12',
'taskstatus':'off'
}
but the result is
{
'taskid': '12',
'taskstatus':'off',
'LEDSub':
{
'taskid': '13',
'taskstatus':'stable',
'LEDSub2':
{
'taskid': '14',
'taskstatus':'burnt'
}
}
I want to just get
{ 'taskid': '12',
'taskstatus':'off'
}
but I dont get why the current route(..) method is not able to do it. The dictionary could be deeply nested and I want to have a generic method. Can someone please help me or guide me on a better way to resolve this issue? Any help is appreciated.
You can use recursion with a generator :
def get_data(d, val):
if any(c == val for c in d.values()):
yield {i:d.get(i) for i in ['taskid', 'taskstatus']}
else:
for i in d.values():
if isinstance(i, dict):
yield from get_data(i, val)
myTodo = {'taskid': '10', 'taskstatus': 'in progress', 'Kitchen': {'Stove': {'LED1': {'taskid': '11', 'taskstatus': 'running'}, 'LED2': {'taskid': '12', 'taskstatus': 'off', 'LEDSub': {'taskid': '13', 'taskstatus': 'stable', 'LEDSub2': {'taskid': '14', 'taskstatus': 'burnt'}}}, 'LED3': {'taskid': '15', 'taskstatus': 'new'}}}}
result = list(get_data(myTodo, '12'))
print(result if not result else result[0])
Output:
{'taskid': '12', 'taskstatus': 'off'}
To format the output a dictionary is created with the same keys contained in the desired output. The yield
statements create a generator object that points to other objects in memory, "generated" on the fly. A generator-based solution is a bit cleaner than using return
, as the latter would require the creation of a list outside the second for
loop to which the returned results from each get_data
call can be appended to. Ultimately, it is cleaner to simply use yield
at each get_data
call.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.