173

I'm trying to loop through a dictionary and print out all key value pairs where the value is not a nested dictionary. If the value is a dictionary I want to go into it and print out its key value pairs, etc.

I tried this. But it only works for the first two levels. I need it to work for any number of levels.

for k, v in d.iteritems():
    if type(v) is dict:
        for t, c in v.iteritems():
            print "{0} : {1}".format(t, c)

I also tried this. It still only prints one thing.

def printDict(d):
    for k, v in d.iteritems():
        if type(v) is dict:
            printDict(v)
        else:
            print "{0} : {1}".format(k, v)

Full test case:

Dictionary:

{u'xml': {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'},
      u'port': u'11'}}

Result:

xml : {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'}, u'port': u'11'}
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Takkun
  • 6,131
  • 16
  • 52
  • 69
  • 1
    Sounds like you want recursion, but the description is not clear enough to be sure. What about some example in-/output? Also, what's wrong with your code? – Niklas B. May 25 '12 at 14:42
  • Will there be only one level of nesting, or is the nesting depth arbitrary? – senderle May 25 '12 at 14:42
  • @Takkun: Then you definitely need recursion. – Niklas B. May 25 '12 at 14:43
  • @NiklasB. Nope, loop and recursion are equal potent. – schlamar May 25 '12 at 14:46
  • what happens if value is not a dictionary? will you ever print keys from higher levels (`k` in your code)? – betabandido May 25 '12 at 14:46
  • 2
    There is a fixed recursion limit in Python: http://docs.python.org/library/sys.html#sys.setrecursionlimit – Dr. Jan-Philip Gehrcke May 25 '12 at 14:47
  • @ms4py But they are not equally suitable for every task – Marcin May 25 '12 at 14:47
  • @Jan-PhilipGehrcke Iteration is not a good idea on physical computers, because there is a fixed amount of memory. – Marcin May 25 '12 at 14:47
  • @ms4py: Yeah, you can also do it with a manual stack data structure or similar. It's just not very practical, but of course it's not impossible – Niklas B. May 25 '12 at 14:47
  • possible duplicate of [How to extract unique values from nested dictionary with Python?](http://stackoverflow.com/questions/2244795/how-to-extract-unique-values-from-nested-dictionary-with-python) – Marcin May 25 '12 at 14:47
  • Also, http://stackoverflow.com/questions/6885335/nested-dictionary-with-duplicate-keys-but-different-values and every other question tagged [python] and having "nested dictionary" in the title. – Marcin May 25 '12 at 14:48
  • 3
    @Jan-PhilipGehrcke: To implement algorithms on a tree-like data structure without recursion is plain suicide. – Niklas B. May 25 '12 at 14:48
  • @Takkun one thing ? what do you mean ? – Scharron May 25 '12 at 14:49
  • The comment about the recursion limit was just meant as an additional information. Removed my assessment. – Dr. Jan-Philip Gehrcke May 25 '12 at 14:50
  • @Takkun Please show your test case in full. – Marcin May 25 '12 at 14:50
  • @Takkun here, you are using lists, not dictionnaries. – Scharron May 25 '12 at 14:52
  • @Takkung: That's not even Python... – Niklas B. May 25 '12 at 14:52
  • sorry i typed that wrong. I edited the OP with a pprint of the dictionary i used – Takkun May 25 '12 at 14:53
  • @NiklasB. depends on the representation of the tree. If it is sorted you can use standard tree search methods which is much less memory consuming than recursion. – schlamar May 25 '12 at 14:53
  • 3
    @Takkun: You are using `dict` as a variable name. Don't ever do this (this is why it fails). – Niklas B. May 25 '12 at 14:54
  • @ms4py: Well it's not sorted in this case, it seems – Niklas B. May 25 '12 at 14:55
  • @Niklas B yup, its working now – Takkun May 25 '12 at 14:56
  • @Takkun The test case you gave us is not coherent with what you said. You wrote that you want to only print "key value pairs where the value is not a nested dictionary", but in your test cases, you are printing the nested dicts :-) – Scharron May 25 '12 at 14:56
  • @Scharron: "If the value is a dictionary I want to go into it and print out its key value pairs" – Niklas B. May 25 '12 at 14:57
  • @NiklasB. Yep, its key value pairs, not the key of the subdict mapped to the subdict. – Scharron May 25 '12 at 14:58
  • 3
    @NiklasB., re: "suicide": I just implemented an iterative version of Scharron's algorithm and its just two lines longer and still quite easy to follow. Besides, translating recursion to iteration is often a requirement when going from trees to general graphs. – Fred Foo May 25 '12 at 15:06
  • @larsmans: Hm, I guess I have to take it back then or change the word "suicide" to "counter-intuitive". Maybe functional programming has ruined me forever :P Can you paste the code? I'd be interested how that looks (I guess it would have to use an explicit stack). – Niklas B. May 25 '12 at 15:14

18 Answers18

217

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))
Scharron
  • 17,233
  • 6
  • 44
  • 63
  • 9
    small improvement. add print(k), before calling myprint(v). – Naomi Fridman Mar 14 '19 at 11:04
  • 4
    This doesn't work with lists. It answers the question, but I assume the asker implicitly was asking for something that properly traverses a list of dicts inside your dict as well, and that isn't covered in this implementation. – Tobias Feil Feb 23 '22 at 11:43
69

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)
tengr
  • 810
  • 7
  • 6
  • 5
    Re: `visited.add(k)`: looks like using keys to check if a dict was already traversed isn't a good idea. The same key name could be used elsewhere in the hierarchy and we will end up skipping those. We should use the value instead. – codeforester Nov 24 '20 at 03:06
  • @codeforester you are absolutely right about not using the keys as indicators! I myself tend to have data for several systems in nested dicts that all have the same keys for certain properties... – BUFU Feb 18 '21 at 12:41
  • 3
    @codeforester aren't keys unique in any particular `dict`? The `visited` stack is not global. A new instance of `visited` is created for every dict. So I think this code would work on any python nested dict even if inner dicts use the same keys as outer dicts. Do you have an example nested dict that breaks this code? – hobs Sep 23 '21 at 09:25
  • 1
    @hobs `{"a1": {"b1": {"c1": 10, "c2": 20}, "b2": 6}, "a2": 5, "a3": 7, "b1": {"d4": 9}}` produces the output `d4: 9 a3: 7 a2: 5 b2: 6 ` – pauleohare Feb 27 '22 at 16:34
  • Which is obviously wrong. – pauleohare Feb 27 '22 at 16:42
  • In order for this solution to work, the *path* to the value would have to be the key in the "visited" list. I use a "path" regularly to say: "go find the value that lives at "key1/key1.1". Each "leaf" in a dictionary should be able to be addressed by such a path. – horace Mar 13 '23 at 19:53
46

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 collections
def 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 abc
def 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
Community
  • 1
  • 1
senderle
  • 145,869
  • 36
  • 209
  • 233
  • 3
    `isinstance(item, collections.Iterable)` is no guarantee for `hasattr(item, "iteritems")`. Checking for `collections.Mapping` is better. – Fred Foo May 25 '12 at 14:55
  • 1
    @larsmans, you're quite right, of course. I was thinking that using `Iterable` would make this solution more generalized, forgetting that, obviously, iterables don't necessarily have `iteritems`. – senderle May 25 '12 at 14:56
  • +1 to this answer because its a general solution that works for this problem, but it's not restricted to just printing the values. @Takkun you should definitely consider this option. In the long run you'll want more than just print the values. – Alejandro Piad May 25 '12 at 14:59
  • 2
    @Seanny123, Thanks for drawing my attention to this. Python 3 changes the picture in a couple of ways, in fact -- I'm going to rewrite this as a version that uses the new `yield from` syntax. – senderle Apr 20 '17 at 12:56
  • This is a great answer. I've added code so that the key (in key, value), will contain the full JSON "path" to the value (leaf). Given the answer's example JSON, the output of my method would be: [('a.e.g', 4), ('a.e.f', 3), ('a.b.c', 1), ('a.b.d', 2), ('a.h.i', 5), ('a.h.j', 6)] ... without this mod, a simple 'g' key could be found many places in any random JSON object. – horace Apr 26 '23 at 12:54
31

Alternative iterative solution:

def myprint(d):
    stack = d.items()
    while stack:
        k, v = stack.pop()
        if isinstance(v, dict):
            stack.extend(v.iteritems())
        else:
            print("%s: %s" % (k, v))
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 2
    Yeah, that's how I imagined it to look like. Thanks. So the advantage of this is that it won't overflow the stack for extremely deep nestings? Or is there something else to it? – Niklas B. May 25 '12 at 15:23
  • @NiklasB.: yep, that's the first benefit. Also, this version can be adapted to different traversal orders quite easily by replacing the stack (a `list`) by a `deque` or even a priority queue. – Fred Foo May 25 '12 at 15:30
  • Yes, but this solution is more space consuming than mine and the recursive one. – schlamar May 25 '12 at 15:35
  • 1
    @ms4py: It's the minimal version that can't overflow. Who cares about those few wasted bytes? Remember that we are talking about Python here – Niklas B. May 25 '12 at 15:39
  • @ms4py: for high branching factors, yes. I'm willing to bet, though, that for small branching factors, this one is faster. – Fred Foo May 25 '12 at 15:41
  • Ok, I go with this bet :-) If you are on `timeit`, compare the recursive version, too :-) – schlamar May 25 '12 at 15:43
  • 1
    @ms4py: For fun, I created [a benchmark](http://pastie.org/3966970). On my computer, the recursive version is fastest and larsmans is second for all three test dictionaries. The version using generators is relatively slow, as expected (because it has to do a lot of juggling with the different generator contexts) – Niklas B. May 25 '12 at 16:19
  • @NiklasB.: nice! However, to be fair, there is a simple opportunity to shave 10-20% off the runtime of ms4py's version, which makes the difference with my version less dramatic. The slowdown of iterative vs. recursive is to be expected because of allocator overhead. – Fred Foo May 25 '12 at 21:16
  • @larsmans: Hm I thought the overhead of the iterative approach would come from the additional interpretation overhead as well as additional attribute lookup and function calls. The array resizing might be another factor, but I don't think it's that important. – Niklas B. May 25 '12 at 21:54
  • It's very cool if it works for the case when nested dicts have the same keys. – sergzach May 27 '19 at 16:44
  • The solution does not work correctly. Try: `myprint({1: 2, 3: {4: 5}, 6: 7})`. – sergzach May 27 '19 at 16:54
  • Python 3 errors with: 'dict_items' object has no attribute 'pop' – Mike T Aug 25 '23 at 03:37
18

Slightly different version I wrote that keeps track of the keys along the way to get there

def print_dict(v, prefix=''):
    if isinstance(v, dict):
        for k, v2 in v.items():
            p2 = "{}['{}']".format(prefix, k)
            print_dict(v2, p2)
    elif isinstance(v, list):
        for i, v2 in enumerate(v):
            p2 = "{}[{}]".format(prefix, i)
            print_dict(v2, p2)
    else:
        print('{} = {}'.format(prefix, repr(v)))

On your data, it'll print

data['xml']['config']['portstatus']['status'] = u'good'
data['xml']['config']['target'] = u'1'
data['xml']['port'] = u'11'

It's also easy to modify it to track the prefix as a tuple of keys rather than a string if you need it that way.

Nippey
  • 4,708
  • 36
  • 44
Ehsan Kia
  • 1,475
  • 18
  • 26
10

Here is pythonic way to do it. This function will allow you to loop through key-value pair in all the levels. It does not save the whole thing to the memory but rather walks through the dict as you loop through it

def recursive_items(dictionary):
    for key, value in dictionary.items():
        if type(value) is dict:
            yield (key, value)
            yield from recursive_items(value)
        else:
            yield (key, value)

a = {'a': {1: {1: 2, 3: 4}, 2: {5: 6}}}

for key, value in recursive_items(a):
    print(key, value)

Prints

a {1: {1: 2, 3: 4}, 2: {5: 6}}
1 {1: 2, 3: 4}
1 2
3 4
2 {5: 6}
5 6
Dmitry Torba
  • 3,004
  • 1
  • 14
  • 24
8

A alternative solution to work with lists based on Scharron's solution

def myprint(d):
    my_list = d.iteritems() if isinstance(d, dict) else enumerate(d)

    for k, v in my_list:
        if isinstance(v, dict) or isinstance(v, list):
            myprint(v)
        else:
            print u"{0} : {1}".format(k, v)
Gabriel
  • 265
  • 3
  • 10
4

I am using the following code to print all the values of a nested dictionary, taking into account where the value could be a list containing dictionaries. This was useful to me when parsing a JSON file into a dictionary and needing to quickly check whether any of its values are None.

    d = {
            "user": 10,
            "time": "2017-03-15T14:02:49.301000",
            "metadata": [
                {"foo": "bar"},
                "some_string"
            ]
        }


    def print_nested(d):
        if isinstance(d, dict):
            for k, v in d.items():
                print_nested(v)
        elif hasattr(d, '__iter__') and not isinstance(d, str):
            for item in d:
                print_nested(item)
        elif isinstance(d, str):
            print(d)

        else:
            print(d)

    print_nested(d)

Output:

    10
    2017-03-15T14:02:49.301000
    bar
    some_string
sigma
  • 216
  • 1
  • 4
  • I have much similar issue here https://stackoverflow.com/questions/50642922/traverse-nested-dictionary-from-depth-and-move-to-top. Is there a way to find the last element of the list of the dictionary, delete that and then move a level up? If not delete, I want to make a list where last element is depth of the data so I reverse the list and delete – Heenashree Khandelwal Jun 29 '18 at 12:38
  • thank you for this solution i was trying to parse yaml but list inside yaml cant be parsed, but this answer perfectly solves the problem – zaheer May 05 '21 at 20:58
4

Your question already has been answered well, but I recommend using isinstance(d, collections.Mapping) instead of isinstance(d, dict). It works for dict(), collections.OrderedDict(), and collections.UserDict().

The generally correct version is:

def myprint(d):
    for k, v in d.items():
        if isinstance(v, collections.Mapping):
            myprint(v)
        else:
            print("{0} : {1}".format(k, v))
Jonas
  • 41
  • 5
2

Iterative solution as an alternative:

def traverse_nested_dict(d):
    iters = [d.iteritems()]

    while iters:
        it = iters.pop()
        try:
            k, v = it.next()
        except StopIteration:
            continue

        iters.append(it)

        if isinstance(v, dict):
            iters.append(v.iteritems())
        else:
            yield k, v


d = {"a": 1, "b": 2, "c": {"d": 3, "e": {"f": 4}}}
for k, v in traverse_nested_dict(d):
    print k, v
schlamar
  • 9,238
  • 3
  • 38
  • 76
  • How is that? Big O should be the same (it's `O(depth)` for the recursive solution. The same applies to this version, if I am thinking correctly). – Niklas B. May 25 '12 at 15:19
  • "Copy the stack"? What are you talking about? Every function call creates a new stackframe. Your solution uses `iters` as an explicit stack, so Big-O memory consumption is the same, or am I missing something? – Niklas B. May 25 '12 at 15:21
  • @NiklasB. Recursion always comes with overhead, see this section at Wikipedia for details: http://en.wikipedia.org/wiki/Recursion_(computer_science)#Expressive_power The stack frame of the recursive solution is much bigger. – schlamar May 25 '12 at 15:25
  • You must be misunderstanding that paragraph. It doesn't say anything to support your statements. – Niklas B. May 25 '12 at 15:27
  • This solution seems to require O(d) extra space whereas mine would take O(bd) space where b is the branching factor (avg. no. of items per `dict`). I don't think it requires significantly less space than the recursive version, though. – Fred Foo May 25 '12 at 15:28
  • "in which all function calls, including tail calls, cause stack allocation that would not occur with the use of looping constructs" – schlamar May 25 '12 at 15:28
  • @ms4py: You are explicitly making the exact same allocations in this code (`iters.append`), so this is not any better memory-usage-wise. What this paragraph is trying to tell you is that certain types of recursive patterns can be transformed into equivalent iterative constructs which consume less memory. This is not one of those types. – Niklas B. May 25 '12 at 15:28
  • 1
    @NiklasB. No, because the stack frame here is only the iter and for the recursive solution the stack frame has the iter, program counter, the variable environment, etc... – schlamar May 25 '12 at 15:31
  • @ms4py: Those are constant factors that don't change Big-O. – Niklas B. May 25 '12 at 15:32
  • But not in a significant way. That would be `depth * sizeof(void*)` "wasted" bytes, which probably will never exceed 1KB even with very deep nestings. Compare this cost to the difference in readability and it's clear which version to prefer... – Niklas B. May 25 '12 at 15:36
  • @NiklasB. If you don't know the depth of your data and it could be >1000 or more there is only one working version ;-) – schlamar May 25 '12 at 15:39
  • @ms4py: No, larsmans' much simpler version would work just as well. But hey, I will not waste any more time discussion this... – Niklas B. May 25 '12 at 15:41
  • @NiklasB: in his last comment, with "only one working version", ms4py for sure meant the iterative approach in contrast to the recursive one and not his individual solution. I think it's good that we've seen that this problem can be tackled using both approaches and we can all be happy. – Dr. Jan-Philip Gehrcke May 25 '12 at 15:48
2

I find this approach a bit more flexible, here you just providing generator function that emits key, value pairs and can be easily extended to also iterate over lists.

def traverse(value, key=None):
    if isinstance(value, dict):
        for k, v in value.items():
            yield from traverse(v, k)
    else:
        yield key, value

Then you can write your own myprint function, then would print those key value pairs.

def myprint(d):
    for k, v in traverse(d):
        print(f"{k} : {v}")

A test:

myprint({
    'xml': {
        'config': {
            'portstatus': {
                'status': 'good',
            },
            'target': '1',
        },
        'port': '11',
    },
})

Output:

status : good
target : 1
port : 11

I tested this on Python 3.6.

sirex
  • 4,593
  • 2
  • 32
  • 21
1

Here's a modified version of Fred Foo's answer for Python 2. In the original response, only the deepest level of nesting is output. If you output the keys as lists, you can keep the keys for all levels, although to reference them you need to reference a list of lists.

Here's the function:

def NestIter(nested):
    for key, value in nested.iteritems():
        if isinstance(value, collections.Mapping):
            for inner_key, inner_value in NestIter(value):
                yield [key, inner_key], inner_value
        else:
            yield [key],value

To reference the keys:

for keys, vals in mynested: 
    print(mynested[keys[0]][keys[1][0]][keys[1][1][0]])

for a three-level dictionary.

You need to know the number of levels before to access multiple keys and the number of levels should be constant (it may be possible to add a small bit of script to check the number of nesting levels when iterating through values, but I haven't yet looked at this).

1

Nested dictionaries looping using isinstance() and yield function. **isinstance is afunction that returns the given input and reference is true or false as in below case dict is true so it go for iteration. **Yield is used to return from a function without destroying the states of its local variable and when the function is called, the execution starts from the last yield statement. Any function that contains a yield keyword is termed a generator.

students= {'emp1': {'name': 'Bob', 'job': 'Mgr'},
     'emp2': {'name': 'Kim', 'job': 'Dev','emp3': {'namee': 'Saam', 'j0ob': 'Deev'}},
     'emp4': {'name': 'Sam', 'job': 'Dev'}}
def nested_dict_pairs_iterator(dict_obj):
     for key, value in dict_obj.items():
        # Check if value is of dict type
        if isinstance(value, dict):
            # If value is dict then iterate over all its values
            for pair in  nested_dict_pairs_iterator(value):
                yield (key, *pair)
        else:
            # If value is not dict type then yield the value
            yield (key, value)
for pair in nested_dict_pairs_iterator(students):
    print(pair)
manjunath
  • 37
  • 4
1

For a ready-made solution install ndicts

pip install ndicts

Import a NestedDict in your script

from ndicts.ndicts import NestedDict

Initialize

dictionary = {
    u'xml': {
        u'config': {
            u'portstatus': {u'status': u'good'}, 
            u'target': u'1'
        },
    u'port': u'11'
    }
}

nd = NestedDict(dictionary)

Iterate

for key, value in nd.items():
    print(key, value)
edd313
  • 1,109
  • 7
  • 20
1

While the original solution from @Scharron is beautiful and simple, it cannot handle the list very well:

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

So this code can be slightly modified like this to handle list in elements:

def myprint(d):
    for k, v in d.items():
        if isinstance(v, dict):
            myprint(v)
        elif isinstance(v, list):
            for i in v:
                myprint(i)
        else:
            print("{0} : {1}".format(k, v))
Sung Kim
  • 8,417
  • 9
  • 34
  • 42
0

These answers work for only 2 levels of sub-dictionaries. For more try this:

nested_dict = {'dictA': {'key_1': 'value_1', 'key_1A': 'value_1A','key_1Asub1': {'Asub1': 'Asub1_val', 'sub_subA1': {'sub_subA1_key':'sub_subA1_val'}}},
                'dictB': {'key_2': 'value_2'},
                1: {'key_3': 'value_3', 'key_3A': 'value_3A'}}

def print_dict(dictionary):
    dictionary_array = [dictionary]
    for sub_dictionary in dictionary_array:
        if type(sub_dictionary) is dict:
            for key, value in sub_dictionary.items():
                print("key=", key)
                print("value", value)
                if type(value) is dict:
                    dictionary_array.append(value)



print_dict(nested_dict)
Jortega
  • 3,616
  • 1
  • 18
  • 21
0

You can print recursively with a dictionary comprehension:

def print_key_pairs(d):
    {k: print_key_pairs(v) if isinstance(v, dict) else print(f'{k}: {v}') for k, v in d.items()}

For your test case this is the output:

>>> print_key_pairs({u'xml': {u'config': {u'portstatus': {u'status': u'good'}, u'target': u'1'}, u'port': u'11'}})
status: good
target: 1
port: 11
rleelr
  • 1,854
  • 17
  • 26
0

Returns a tuple of each key and value and the key contains the full path

from typing import Mapping, Tuple, Iterator

def traverse_dict(nested: Mapping, parent_key="", keys_to_not_traverse_further=tuple()) -> Iterator[Tuple[str, str]]:
    """Each key is joined with it's parent using dot as a separator.

    Once a `parent_key` matches `keys_to_not_traverse_further` 
   it will no longer find its child dicts.
   """
    for key, value in nested.items():
        if isinstance(value, abc.Mapping) and key not in keys_to_not_traverse_further:
            yield from traverse_dict(value, f"{parent_key}.{key}", keys_to_not_traverse_further)
        else:
            yield f"{parent_key}.{key}", value

Let's test it

my_dict = {
    "isbn": "123-456-222",
    "author": {"lastname": "Doe", "firstname": "Jane"},
    "editor": {"lastname": "Smith", "firstname": "Jane"},
    "title": "The Ultimate Database Study Guide",
    "category": ["Non-Fiction", "Technology"],
    "first": {
        "second": {"third": {"fourth": {"blah": "yadda"}}},
        "fifth": {"sixth": "seventh"},
    },
}
for k, v in traverse_dict(my_dict):
    print(k, v)

Returns

.isbn 123-456-222
.author.lastname Doe
.author.firstname Jane
.editor.lastname Smith
.editor.firstname Jane
.title The Ultimate Database Study Guide
.category ['Non-Fiction', 'Technology']
.first.second.third.fourth.blah yadda
.first.fifth.sixth seventh

If you don't care about some child dicts e.g names in this case then use the keys_to_not_traverse_further

for k, v in traverse_dict(my_dict, parent_key="", keys_to_not_traverse_further=("author","editor")):
    print(k, v)

Returns

.isbn 123-456-222
.author {'lastname': 'Doe', 'firstname': 'Jane'}
.editor {'lastname': 'Smith', 'firstname': 'Jane'}
.title The Ultimate Database Study Guide
.category ['Non-Fiction', 'Technology']
.first.second.third.fourth.blah yadda
.first.fifth.sixth seventh
Mark
  • 1,337
  • 23
  • 34