1

I would like to print nested hash in Python, from this:

example_dict = {
    'key1' : 'value1',
    'key2' : {
        'key2a': 'value2a'
    },
    'key3' : {
        'key3a': {
            'key3aa': 'value3aa',
            'key3ab': 'value3ab',
            'key3ac': 'value3ac'
        },
        'key3b': [
            'value3b1',
            'value3b2'
        ]
    },
}

to something like this:

key1 value1
key2 key2a value2a
key3 key3a key3aa value3aa
key3 key3a key3ab value3ab
key3 key3a key3ac value3ac
key3 key3b value3b1
key3 key3b value3b2

I am not really familiar with Python, and after couple hours I'm still stuck.

I currently working on this function:

def recursive(src, res, line = ''):
    if isinstance(src, dict):
        for key, value in src.items():
            line += str(' ' + key)
            recursive(value, res, line)
    elif isinstance(src, list):
        for litem in src:
            recursive(litem, res, line)
    else:
        line += str(' ' + str(src))
        res.append(line)

I'm trying to add each line to a list to be able to use it later, but I think I don't have the right aproach with my shared variable (line), because at each dict the recursive call is made with one more dict key from the same level of depth, so I got this :

 key3 key3a key3aa value3aa
 key3 key3a key3aa key3ac value3ac
 key3 key3a key3aa key3ac key3ab value3ab
 key3 key3a key3b value3b1
 key3 key3a key3b value3b2
 key3 key2 key2a value2a
 key3 key2 key1 value1

What would be a Pythonic way to do this?

Stephen Rauch
  • 40,722
  • 30
  • 82
  • 105
MaxGeneGrim
  • 153
  • 1
  • 2
  • 8

3 Answers3

6

Using a generator (yield statement) you can do that recursively like:

Code:

def recursive(src, so_far=()):
    if isinstance(src, dict):
        for key, value in src.items():
            yield from recursive(value, so_far + (key,))
    elif isinstance(src, list):
        for item in src:
            yield from recursive(item, so_far)
    else:
        yield ' '.join(so_far + (src,))

One thing to note is the need to re-yield the results from the generator that was recursed into. That is done here using:

yield from recursive(item, so_far)

Which is equivalent to:

for i in recursive(item, so_far):
    yield i

Test Code:

example_dict = {
    'key1': 'value1',
    'key2': {
        'key2a': 'value2a'
    },
    'key3': {
        'key3a': {
            'key3aa': 'value3aa',
            'key3ab': 'value3ab',
            'key3ac': 'value3ac'
        },
        'key3b': [
            'value3b1',
            'value3b2'
        ]
    },
}

for line in recursive(example_dict):
    print(line)

Results:

key1 value1
key2 key2a value2a
key3 key3a key3aa value3aa
key3 key3a key3ab value3ab
key3 key3a key3ac value3ac
key3 key3b value3b1
key3 key3b value3b2
Stephen Rauch
  • 40,722
  • 30
  • 82
  • 105
  • 1
    Thanks for the nice answer. I wonder if it would be worth mentioning the `yield from` construct in the context of the two `for ...: yield...` blocks? – NPE May 13 '18 at 15:01
  • 1
    Thanks for the answer and for the links, that helps a lot ! I also looking at the `yield from` as mentioned above by curiosity, but your solution works like a charm in my case. Again Thank you ! – MaxGeneGrim May 13 '18 at 17:09
1

I got this to work, but its a bit hacky, using append adds one element, and extend adds all elements in a list. Hope it helps:

def recursive(src, start=""):
    lst = []
    for k, v in src.items():
        new_start = "{} {}".format(start, k)
        if isinstance(v, dict):
            lst.extend(recursive(v, start=new_start))
        elif isinstance(v, list):
            lst.extend("{}: {}".format(new_start[1:] , val) for val in v)
        else:
            lst.append("{}: {}".format(new_start[1:] , v))

    return lst


items = recursive(example_dict)
for item in items:
    print(item)

Output:

 key1: value1
 key2 key2a: value2a
 key3 key3a key3aa: value3aa
 key3 key3a key3ab: value3ab
 key3 key3a key3ac: value3ac
 key3 key3b: value3b1
 key3 key3b: value3b2
ktzr
  • 1,477
  • 1
  • 9
  • 23
0

Pretty straightforward recursive implementation.

def pr(t, pref=''):
    for k, v in t.items():
        if isinstance(v, dict):
            pr(v, pref=(pref + k + ' '))
        elif isinstance(v, list):
            for el in v:
                if isinstance(el, str):
                    print(pref + ' ' + k + ' ' + el)
                else:
                    pr(el, pref=k + ' ')
        else:
            print(pref + ' ' + k + ' ' + v)
xrisk
  • 3,400
  • 18
  • 39