Iterate through dict with nested lists with nested dicts … and so on

I’m using creopyson to read a 3D model’s bill of materials. The data that creopyson returns is a nested dict with lists. The BOM is structured in a parent -> child structure where there can be multiple children (organized in a list). Each child is a dict containing the child’s info. Ideally, I’d like to iterate through each dict entry no matter its position in the ‘master’ dict. Then I’ll insert each entry into a tree view as a structured BOM based on its position in the dict or by its root level (key->seq_path).

Here’s a sample of the raw output dict from creopyson:

{'has_simprep': False, 'children': {'children': [{'file': 'COUPLERLARGE_SIDE_UPPERPLATE.PRT', 'seq_path': 'root.1'}, {'file': 'SPRING_REF.PRT', 'seq_path': 'root.2'}, {'file': 'CYLINDER_REF.PRT', 'seq_path': 'root.3'}, {'file': 'SPRING_REF.PRT', 'seq_path': 'root.4'}, {'file': 'MOUNTBLOCK.PRT', 'seq_path': 'root.5'}, {'file': 'SLIDERPLATE.PRT', 'seq_path': 'root.6'}, {'file': 'STATICHOOK_BACKING.PRT', 'seq_path': 'root.7'}, {'file': 'PIVOTMECH.PRT', 'seq_path': 'root.8'}, {'file': 'SPRING_REF.PRT', 'seq_path': 'root.9'}, {'children': [{'file': 'SLIDINGPLATE.PRT', 'seq_path': 'root.10.1'}, {'file': 'HOOK.PRT', 'seq_path': 'root.10.2'}, {'file': 'HOOK_BACKING.PRT', 'seq_path': 'root.10.3'}, {'file': 'HOOK.PRT', 'seq_path': 'root.10.4'}, {'file': 'SHOOK_BACKPLATE.PRT', 'seq_path': 'root.10.5'}, {'file': 'CYLINDERCLEVIS.PRT', 'seq_path': 'root.10.6'}], 'file': 'SLIDINGHOOK.ASM', 'seq_path': 'root.10'}, {'file': 'CYLINDER_REF.PRT', 'seq_path': 'root.11'}], 'file': 'bigcoupler_ref.asm', 'seq_path': 'root'}, 
'generic': '', 'file': 'bigcoupler_ref.asm'}

Here’s a sample of the output from creopyson using pprint to indent:

{   'children': {   'children': [   {   'file': 'COUPLERLARGE_SIDE_UPPERPLATE.PRT',
                                        'seq_path': 'root.1'},
                                    {   'file': 'SPRING_REF.PRT',      
                                        'seq_path': 'root.2'},
                                    {   'file': 'CYLINDER_REF.PRT',    
                                        'seq_path': 'root.3'},
                                    {   'file': 'SPRING_REF.PRT',
                                        'seq_path': 'root.4'},
                                    {   'file': 'MOUNTBLOCK.PRT',
                                        'seq_path': 'root.5'},
                                    {   'file': 'SLIDERPLATE.PRT',
                                        'seq_path': 'root.6'},
                                    {   'file': 'STATICHOOK_BACKING.PRT',
                                        'seq_path': 'root.7'},
                                    {   'file': 'PIVOTMECH.PRT',
                                        'seq_path': 'root.8'},
                                    {   'file': 'SPRING_REF.PRT',
                                        'seq_path': 'root.9'},
                                    {   'children': [   {   'file': 'SLIDINGPLATE.PRT',
                                                            'seq_path': 'root.10.1'},
                                                        {   'file': 'HOOK.PRT',
                                                            'seq_path': 'root.10.2'},
                                                        {   'file': 'HOOK_BACKING.PRT',
                                                            'seq_path': 'root.10.3'},
                                                        {   'file': 'HOOK.PRT',
                                                            'seq_path': 'root.10.4'},
                                                        {   'file': 'SHOOK_BACKPLATE.PRT',
                                                            'seq_path': 'root.10.5'},
                                                        {   'file': 'CYLINDERCLEVIS.PRT',
                                                            'seq_path': 'root.10.6'}],
                                        'file': 'SLIDINGHOOK.ASM',
                                        'seq_path': 'root.10'},
                                    {   'file': 'CYLINDER_REF.PRT',
                                        'seq_path': 'root.11'}],
                    'file': 'bigcoupler_ref.asm',
                    'seq_path': 'root'},
    'file': 'bigcoupler_ref.asm',
    'generic': '',
    'has_simprep': False}

>Solution :

You can define this generator:

def nested_entries(d):
    if "children" in d:
        for child in d["children"]:
            yield from nested_entries(child)
    else:
        yield d

It is a bit odd that your top-level dict has a "children" key which does not have a list as value, like is the case at deeper levels. The above function assumes that "children" keys have lists as values, so call it with the "children" value of the top-level object:

d = {'has_simprep': False, 'children': {'children': [{'file': 'COUPLERLARGE_SIDE_UPPERPLATE.PRT', 'seq_path': 'root.1'}, {'file': 'SPRING_REF.PRT', 'seq_path': 'root.2'}, {'file': 'CYLINDER_REF.PRT', 'seq_path': 'root.3'}, {'file': 'SPRING_REF.PRT', 'seq_path': 'root.4'}, {'file': 'MOUNTBLOCK.PRT', 'seq_path': 'root.5'}, {'file': 'SLIDERPLATE.PRT', 'seq_path': 'root.6'}, {'file': 'STATICHOOK_BACKING.PRT', 'seq_path': 'root.7'}, {'file': 'PIVOTMECH.PRT', 'seq_path': 'root.8'}, {'file': 'SPRING_REF.PRT', 'seq_path': 'root.9'}, {'children': [{'file': 'SLIDINGPLATE.PRT', 'seq_path': 'root.10.1'}, {'file': 'HOOK.PRT', 'seq_path': 'root.10.2'}, {'file': 'HOOK_BACKING.PRT', 'seq_path': 'root.10.3'}, {'file': 'HOOK.PRT', 'seq_path': 'root.10.4'}, {'file': 'SHOOK_BACKPLATE.PRT', 'seq_path': 'root.10.5'}, {'file': 'CYLINDERCLEVIS.PRT', 'seq_path': 'root.10.6'}], 'file': 'SLIDINGHOOK.ASM', 'seq_path': 'root.10'}, {'file': 'CYLINDER_REF.PRT', 'seq_path': 'root.11'}], 'file': 'bigcoupler_ref.asm', 'seq_path': 'root'}, 
'generic': '', 'file': 'bigcoupler_ref.asm'}

for item in nested_entries(d["children"]):
    print(item)

This outputs:

{'file': 'COUPLERLARGE_SIDE_UPPERPLATE.PRT', 'seq_path': 'root.1'}
{'file': 'SPRING_REF.PRT', 'seq_path': 'root.2'}
{'file': 'CYLINDER_REF.PRT', 'seq_path': 'root.3'}
{'file': 'SPRING_REF.PRT', 'seq_path': 'root.4'}
{'file': 'MOUNTBLOCK.PRT', 'seq_path': 'root.5'}
{'file': 'SLIDERPLATE.PRT', 'seq_path': 'root.6'}
{'file': 'STATICHOOK_BACKING.PRT', 'seq_path': 'root.7'}
{'file': 'PIVOTMECH.PRT', 'seq_path': 'root.8'}
{'file': 'SPRING_REF.PRT', 'seq_path': 'root.9'}
{'file': 'SLIDINGPLATE.PRT', 'seq_path': 'root.10.1'}
{'file': 'HOOK.PRT', 'seq_path': 'root.10.2'}
{'file': 'HOOK_BACKING.PRT', 'seq_path': 'root.10.3'}
{'file': 'HOOK.PRT', 'seq_path': 'root.10.4'}
{'file': 'SHOOK_BACKPLATE.PRT', 'seq_path': 'root.10.5'}
{'file': 'CYLINDERCLEVIS.PRT', 'seq_path': 'root.10.6'}
{'file': 'CYLINDER_REF.PRT', 'seq_path': 'root.11'}

Leave a Reply