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