0

I read the answers to how to get all occurrences of a key within nested dicts and lists here:

Link to question

Apart from getting the value of the key, I would like to be able to distinguish values when multiple values are found in the nested structure. That means that the method output should include the "branch-path" to each of the matched values.

o = { 'temparature': '50', 
  'logging': {
    'handlers': {
      'console': {
        'formatter': 'simple', 
        'class': 'logging.StreamHandler', 
        'stream': 'ext://sys.stdout', 
        'level': 'DEBUG'
      }
    },
    'loggers': {
      'simpleExample': {
        'handlers': ['console'], 
        'propagate': 'no', 
        'level': 'INFO'
      },
     'root': {
       'handlers': ['console'], 
       'level': 'DEBUG'
     }
   }, 
   'version': '1', 
   'formatters': {
     'simple': {
       'datefmt': "'%Y-%m-%d %H:%M:%S'", 
       'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
     }
   }
 }, 
 'treatment': {'second': 5, 'last': 4, 'first': 4},   
 'treatment_plan': [[4, 5, 4], [4, 5, 4], [5, 5, 5]]}

So in the dictionary example above, if I wanted the value of "console" I suggest to get a list of tuples containing one element:

[(["logging", "handlers", "console"], 
{'formatter': 'simple', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', 'level': 'DEBUG'})]

Is it possible to include that functionality in the method i linked to?

Mr. Blonde
  • 711
  • 2
  • 12
  • 27
  • is it strictly a nested dict, or could your tree contain a list that your path would require an index into? – JonSG Jun 08 '21 at 16:42
  • IData is converted from json, so it can contain a list, but I do not need the index. We can assume that lists always will be leafs in the tree. – Mr. Blonde Jun 08 '21 at 19:28

1 Answers1

0

After some experimenting I found the below code to solve my problem.

def gen_dict_location_extract(key, value, path=None):
    if path is None:
        path = []

    if hasattr(value, "items"):
        for k, v in value.items():
            if k == key:  # recursive exit point
                if len(path) > 0:
                    yield (v, path)
                else:   # handling root keys
                    yield (v, None)                 

            if isinstance(v, dict):
                path_copy = path.copy()
                # it is important to do a copy of the path for recursive calls
                # so every iteration has its own path object
                path_copy.append(k)
                yield from gen_dict_location_extract(key, v, path_copy)
            elif isinstance(v, list):
                yield from gen_dict_location_extract(key, v, path)


def call_gen_dict_location_extract(key, enumerable):                    
    results = []
    for result in gen_dict_location_extract(key, enumerable):
        results.append(result)
    return results

To test the code you would execute it like this:

call_gen_dict_location_extract("level", o)
Mr. Blonde
  • 711
  • 2
  • 12
  • 27