Home > front end >  Iterating over nested dictionary returns only first element
Iterating over nested dictionary returns only first element

Time:04-06

I have this nested dictionary ("dictionary of dictionaries")

source = {
    "OuterVal0": {"InnerVal": [10, 21, 96],"InnerVal2": [100, 91, 71]},
    "OuterVal1": {"InnerVal": [21, 19, 76],"InnerVal2": [1, 1, 1]},
    "OuterVal2": {"InnerVal": [1, 1, 96],"InnerVal2": [10, 9, 7]},
    "OuterVal3": {"InnerVal": [0, 2, 6],"InnerVal2": [1, 911, 718]},
    "OuterVal4": {"InnerVal": [12, 13, 9],"InnerVal2": [1000, 910, 701]},
    "OuterVal5": {"InnerVal": [110, 211, 961],"InnerVal2": [10, 911, 918]},     
}

And I want to create a new one which would consist of outer keys associated with inner values (see expected output below) I use this recursive function:

def myPrint(d, key=""):
    output = {}
    for k, v in d.items():
        i = 0
        if isinstance(v, dict):
          return  myPrint(v, k)
        else:
            for  value in d.values():
                newkey = (f"{i}_{key}")
                output[newkey] = value
                i  = 1
    return output

But when I try to print that:

print(myPrint(source))

I get this (only the first dictionary is processed:

# {'0_OuterVal0': [10, 21, 96], '1_OuterVal0': [100, 91, 71]}

But I would like to have something like this (all dictionaries processed)

"""
Expected output
{'0_OuterVal0': [10, 21, 96], '1_OuterVal0': [100, 91, 71]}
{'0_OuterVal1':[21, 19, 76], '1_OuterVal1': [1, 1, 1]}
.
.
.
{'0_OuterVal5':  [110, 211, 961], '1_OuterVal5': [10, 911, 918]}

"""

What am I doing wrong?

Thank you very much in advance for any help.

CodePudding user response:

The problem is that when you call return myPrint(v, k) for the first time you compute the values for the first dictionary and then return instead of continuing to the other values in the for loop.

Changing the function to:

def myPrint(d, key=""):
    output = {}
    for k, v in d.items():
        i = 0
        if isinstance(v, dict):
          output.update(myPrint(v, k))
        else:
            for  value in d.values():
                newkey = (f"{i}_{key}")
                output[newkey] = value
                i  = 1
    return output

will return a big dictionary, for you example:

{'0_OuterVal0': [10, 21, 96], '1_OuterVal0': [100, 91, 71],
 '0_OuterVal1': [21, 19, 76], '1_OuterVal1': [1, 1, 1],
 '0_OuterVal2': [1, 1, 96], '1_OuterVal2': [10, 9, 7],
 '0_OuterVal3': [0, 2, 6], '1_OuterVal3': [1, 911, 718],
 '0_OuterVal4': [12, 13, 9], '1_OuterVal4': [1000, 910, 701],
 '0_OuterVal5': [110, 211, 961], '1_OuterVal5': [10, 911, 918]}

However, the function can be nicely packed in a non-recursive way as follow:

output = [{f'{ii}_{k}': vv  for (k, v) in source.items() for ii, (kk, vv) in enumerate(v.items())}]

CodePudding user response:

Your desired output can be obtained more simply as a list comprehension:

output = [{f'0_OuterVal{i}': v['InnerVal'], f'1_OuterVal{i}': v['InnerVal2']}
          for i, (k, v) in enumerate(source.items())]

This will create a list of dicts with the keys and values you specified.

CodePudding user response:

Following the execution of the code step by step with a debugger would help you :

When you call myPrint(source) and enter the for loop, you indeed call your function recursively, but instead of adding the 1st nested dictionary to your output, you use the return keyword. so the dictionary created with the 1st key is returned and that's the end of your function.

If the purpose of your function is to print, and that you want to keep your original code structure, you could use this :

def myPrint(d, key=""):
    output = {}
    lines = ""
    for k, v in d.items():
        i = 0
        if isinstance(v, dict):
            lines  = str(myPrint(v, k))   '\n'
        else:
            for value in d.values():
                newkey = (f"{i}_{key}")
                output[newkey] = value
                i  = 1

    return output if lines == "" else lines
  • Related