Home > Back-end >  Remove the bottom level in a nested python dictionary
Remove the bottom level in a nested python dictionary

Time:11-21

Consider this input dictionary:

my_dict = {
    'group1':{
        'type1': {'val1' : 45, 'val2' : 12, 'val3' : 65},
        'type2': {'val5' : 65, 'val6' : 132, 'val7' : 656},
    },
    'group2':{
        'type3': {'val11' : 45, 'val12' : 123, 'val13' : 3},
        'type4': {'val51' : 1, 'val61' : 2, 'val71' : 3, },
    },
}

I would like to remove the last 'level' (the one that only has numbers), and get something like:

new_dict = {
    'group1':{
        'type1': ['val1', 'val2', 'val3'],
        'type2': ['val5', 'val6', 'val7'],
    },
    'group2':{
        'type3': ['val11', 'val12', 'val13'],
        'type4': ['val51', 'val61', 'val71'],
    },
}

I am currently doing it by manually looping and so on, but I wonder if there is a way to do it more efficiently.

CodePudding user response:

You don't give an example of the actual code you are using to change the dict, but likely you will always need 2 levels of looping to iterate over groups, and then types. Something like:

newdict = {}
for k, grp in my_dict.items():
    newdict[k] = {}
    for typ, val in grp.items():
        newdict[k][typ] = list(val)

One option to be more efficient is to not pre-compute these updted values, but instead when you come to use the 'group' data, wrap the output in list at that point. (Though obviously you will still be iterating when the dict is being used, to get these nested dicts).

CodePudding user response:

If your dictionary doesn't have a fixed number of nesting levels, you could write a recursive function to do it:

def stripBottom(d):
    if not isinstance(d,dict):
        return d
    elif not any(isinstance(v,dict) for v in d.values()):
        return list(d)
    else:
        return {k:stripBottom(v) for k,v in d.items()}

Output:

my_dict = {
    'group1':{
        'type1': {'val1' : 45, 'val2' : 12, 'val3' : 65},
        'type2': {'val5' : 65, 'val6' : 132, 'val7' : 656},
    },
    'group2':{
        'type3': {'val11' : 45, 'val12' : 123, 'val13' : 3},
        'type4': {'val51' : 1, 'val61' : 2, 'val71' : 3, },
    },
}

print(stripBottom(my_dict))

{'group1': {'type1': ['val1', 'val2', 'val3'],
            'type2': ['val5', 'val6', 'val7']},
 'group2': {'type3': ['val11', 'val12', 'val13'],
            'type4': ['val51', 'val61', 'val71']}}

If your dictionary has variable levels of nesting or if you want to drop a specific level, you would need to add a parameter to the function:

def stripBottom(d,level=0):
    if not isinstance(d,dict):
        return d
    elif level == 0:
        return list(d)
    else:
        return {k:stripBottom(v,level-1) for k,v in d.items()}

print(stripBottom(my_dict,1))

{'group1': ['type1', 'type2'], 'group2': ['type3', 'type4']}
  • Related