Loop through all nested dictionary values? Loop through all nested dictionary values? python python

Loop through all nested dictionary values?


As said by Niklas, you need recursion, i.e. you want to define a function to print your dict, and if the value is a dict, you want to call your print function using this new dict.

Something like :

def myprint(d):    for k, v in d.items():        if isinstance(v, dict):            myprint(v)        else:            print("{0} : {1}".format(k, v))


There are potential problems if you write your own recursive implementation or the iterative equivalent with stack. See this example:

    dic = {}    dic["key1"] = {}    dic["key1"]["key1.1"] = "value1"    dic["key2"]  = {}    dic["key2"]["key2.1"] = "value2"    dic["key2"]["key2.2"] = dic["key1"]    dic["key2"]["key2.3"] = dic

In the normal sense, nested dictionary will be a n-nary tree like data structure. But the definition doesn't exclude the possibility of a cross edge or even a back edge (thus no longer a tree). For instance, here key2.2 holds to the dictionary from key1, key2.3 points to the entire dictionary(back edge/cycle). When there is a back edge(cycle), the stack/recursion will run infinitely.

                          root<-------back edge                        /      \           |                     _key1   __key2__      |                    /       /   \    \     |               |->key1.1 key2.1 key2.2 key2.3               |   /       |      |               | value1  value2   |               |                  |               cross edge----------|

If you print this dictionary with this implementation from Scharron

    def myprint(d):      for k, v in d.items():        if isinstance(v, dict):          myprint(v)        else:          print "{0} : {1}".format(k, v)

You would see this error:

    RuntimeError: maximum recursion depth exceeded while calling a Python object

The same goes with the implementation from senderle.

Similarly, you get an infinite loop with this implementation from Fred Foo:

    def myprint(d):        stack = list(d.items())        while stack:            k, v = stack.pop()            if isinstance(v, dict):                stack.extend(v.items())            else:                print("%s: %s" % (k, v))

However, Python actually detects cycles in nested dictionary:

    print dic    {'key2': {'key2.1': 'value2', 'key2.3': {...},        'key2.2': {'key1.1': 'value1'}}, 'key1': {'key1.1': 'value1'}}

"{...}" is where a cycle is detected.

As requested by Moondra this is a way to avoid cycles (DFS):

def myprint(d):     stack = list(d.items())     visited = set()     while stack:         k, v = stack.pop()         if isinstance(v, dict):             if k not in visited:                 stack.extend(v.items())         else:             print("%s: %s" % (k, v))         visited.add(k)


Since a dict is iterable, you can apply the classic nested container iterable formula to this problem with only a couple of minor changes. Here's a Python 2 version (see below for 3):

import collectionsdef nested_dict_iter(nested):    for key, value in nested.iteritems():        if isinstance(value, collections.Mapping):            for inner_key, inner_value in nested_dict_iter(value):                yield inner_key, inner_value        else:            yield key, value

Test:

list(nested_dict_iter({'a':{'b':{'c':1, 'd':2},                             'e':{'f':3, 'g':4}},                        'h':{'i':5, 'j':6}}))# output: [('g', 4), ('f', 3), ('c', 1), ('d', 2), ('i', 5), ('j', 6)]

In Python 2, It might be possible to create a custom Mapping that qualifies as a Mapping but doesn't contain iteritems, in which case this will fail. The docs don't indicate that iteritems is required for a Mapping; on the other hand, the source gives Mapping types an iteritems method. So for custom Mappings, inherit from collections.Mapping explicitly just in case.

In Python 3, there are a number of improvements to be made. As of Python 3.3, abstract base classes live in collections.abc. They remain in collections too for backwards compatibility, but it's nicer having our abstract base classes together in one namespace. So this imports abc from collections. Python 3.3 also adds yield from, which is designed for just these sorts of situations. This is not empty syntactic sugar; it may lead to faster code and more sensible interactions with coroutines.

from collections import abcdef nested_dict_iter(nested):    for key, value in nested.items():        if isinstance(value, abc.Mapping):            yield from nested_dict_iter(value)        else:            yield key, value