It is to everyone, not just to the last. There is a solution, but I would like to see an elegant option.

Test dictionary:

test = {'a': {'b': {'c': {},'d': {'e': {}}}}} 

Due conclusion:

 [('a',), ('a', 'b'), ('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'b', 'd', 'e')] 

My decision:

First, find the path to the final key of each nested dictionary:

 def recurse(inp_dict, path=()): if inp_dict: for key in inp_dict: for rv in recurse(inp_dict[key], path + (key,)): yield rv else: yield path after_recurse = recurse(test) list(after_recurse) [Out]: [('a', 'b', 'c'), ('a', 'b', 'd', 'e')] 

Next, we shorten each resulting tuple by one last element in the loop, add to the existing tuples, and uniquely list:

 result = set([x[:y + 1] for x in after_recurse for y in range(len(x))]) sorted(result) [Out]: [('a',), ('a', 'b'), ('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'b', 'd', 'e')] 
  • Does it make sense to add an inspection code tag to this question? - mymedia
  • The @mymedia code-review label requires a working code. The code in the question does not produce the expected output without additional processing - jfs

3 answers 3

To support arbitrary types, and not just dictionaries as values, you can test the presence of the items attribute:

 def paths(some_dict, path=()): for key, value in some_dict.items(): key_path = path + (key,) yield key_path if hasattr(value, 'items'): yield from paths(value, key_path) 

Example:

 d = {'a': {'b': {'c': {},'d': {'e': {}}}}} print(*paths(d), sep='\n') 

Result :

 ('a',) ('a', 'b') ('a', 'b', 'd') ('a', 'b', 'd', 'e') ('a', 'b', 'c') 

Dictionaries are unordered in Python, so the order of output can vary depending on the environment, the version of Python and even from launch to launch.

Slightly sacrificing readability and DRY, you can slightly reduce the code:

 def paths(some_dict, path=()): for key, value in getattr(some_dict, 'items', lambda: ())(): yield path + (key,) yield from paths(value, path + (key,)) 

The result is the same.

    Another alternative answer:

     def foo(root, result, path=()): for k, v in root.items(): new_path = path + (k,) result.append(new_path) foo(v, result, new_path) test = {'a': {'b': {'c': {}, 'd': {'e': {}}}}} result = [] foo(test, result) print(result) 

    Console:

     [('a',), ('a', 'b'), ('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'b', 'd', 'e')] 

      Using recursion:

       from copy import deepcopy result = [] def theway(fork, part): if fork: for key in fork.keys(): way = deepcopy(part) way.extend(key) global result result.append(tuple(way)) theway(fork[key], way) test = {'a': {'b': {'c': {},'d': {'e': {}}}}} theway(test, []) print(sorted(result)) >>> [('a',), ('a', 'b'), ('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'b', 'd', 'e')] 
      • If result passed to a function, you can do without a global variable - gil9red