Flatten nested dictionaries, compressing keys

前端 未结 28 2246
遇见更好的自我
遇见更好的自我 2020-11-22 01:16

Suppose you have a dictionary like:

{\'a\': 1,
 \'c\': {\'a\': 2,
       \'b\': {\'x\': 5,
             \'y\' : 10}},
 \'d\': [1, 2, 3]}

Ho

28条回答
  •  孤街浪徒
    2020-11-22 01:49

    I tried some of the solutions on this page - though not all - but those I tried failed to handle the nested list of dict.

    Consider a dict like this:

    d = {
            'owner': {
                'name': {'first_name': 'Steven', 'last_name': 'Smith'},
                'lottery_nums': [1, 2, 3, 'four', '11', None],
                'address': {},
                'tuple': (1, 2, 'three'),
                'tuple_with_dict': (1, 2, 'three', {'is_valid': False}),
                'set': {1, 2, 3, 4, 'five'},
                'children': [
                    {'name': {'first_name': 'Jessica',
                              'last_name': 'Smith', },
                     'children': []
                     },
                    {'name': {'first_name': 'George',
                              'last_name': 'Smith'},
                     'children': []
                     }
                ]
            }
        }
    

    Here's my makeshift solution:

    def flatten_dict(input_node: dict, key_: str = '', output_dict: dict = {}):
        if isinstance(input_node, dict):
            for key, val in input_node.items():
                new_key = f"{key_}.{key}" if key_ else f"{key}"
                flatten_dict(val, new_key, output_dict)
        elif isinstance(input_node, list):
            for idx, item in enumerate(input_node):
                flatten_dict(item, f"{key_}.{idx}", output_dict)
        else:
            output_dict[key_] = input_node
        return output_dict
    

    which produces:

    {
      owner.name.first_name: Steven,
      owner.name.last_name: Smith,
      owner.lottery_nums.0: 1,
      owner.lottery_nums.1: 2,
      owner.lottery_nums.2: 3,
      owner.lottery_nums.3: four,
      owner.lottery_nums.4: 11,
      owner.lottery_nums.5: None,
      owner.tuple: (1, 2, 'three'),
      owner.tuple_with_dict: (1, 2, 'three', {'is_valid': False}),
      owner.set: {1, 2, 3, 4, 'five'},
      owner.children.0.name.first_name: Jessica,
      owner.children.0.name.last_name: Smith,
      owner.children.1.name.first_name: George,
      owner.children.1.name.last_name: Smith,
    }
    

    A makeshift solution and it's not perfect.
    NOTE:

    • it doesn't keep empty dicts such as the address: {} k/v pair.

    • it won't flatten dicts in nested tuples - though it would be easy to add using the fact that python tuples act similar to lists.

提交回复
热议问题