简体   繁体   中英

Get the values of a nested dictionary in python

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM