【问题标题】:Flatten Dictionary with select/ignore specific keys使用选择/忽略特定键展平字典
【发布时间】:2020-01-27 22:21:24
【问题描述】:

问题

给定一个具有多个级别的 dict,根据 key 的指定路径将 dict 展平。

样本输入数据

input_data = [
    {
        "CreatedBy": {"Name":"User001"},
        "Lookup": {
            "TextField": "Some text",
            "UserField": {"Id": "ID001", "Name": "Name001"},
            "CreatedBy": {"Name": "User001"},
        },
        "Image": {"a": "b"},
    }
]

测试用例

测试用例 1

仅在指定路径匹配时展平

output = flatten_dict(input_data, use_keys=["Image", "Lookup.CreatedBy", "CreatedBy"])

expected = [{
    'CreatedBy.Name':'User001'},
    "Lookup": {
        "TextField": "Some text",
        "UserField": {"Id": "ID001", "Name": "Name001"},
        "CreatedBy.Name": "User001",
    },
    "Image.a": "b",
}]

测试用例 2

output = flatten_dict(input_data, use_keys=["Image", "Lookup.CreatedBy"])

expected = [{
    "CreatedBy": {"Name":"User001"},
    "Lookup": {
        "TextField": "Some text",
        "UserField": {"Id": "ID001", "Name": "Name001"},
        "CreatedBy.Name": "User001",
    },
    "Image.a": "b",
}]

测试用例 3 - 顶级键优先 展平给定父路径的所有子路径。即,仅给定“查找”,解决方案会扁平化为 CreatedBy.Name 而没有明确提及。

output = flatten_dict(input_data, use_keys=["Image", "Lookup.CreatedBy", "Lookup"])

expected = [{
    "CreatedBy": {"Name":"User001"}
    "Lookup.TextField": "Some text",
    "Lookup.UserField.Id": "ID001", 
    "Lookup.UserField.Name": "Name001",
    "Lookup.CreatedBy.Name": "User001",
    "Image.a": "b",
}]

这是我尝试过的

目前,我将解决方案限制为单个 dict,稍后我想将其扩展为 dict 列表。

def flatten(data, prev_key="", level=0, use_keys=["Image", "CreatedBy"]):
    if isinstance(data, list):
        data = data[0]
    res = {}
    for k, v in data.items():

        if level == 0:
            newkey = k
        else:
            newkey = prev_key + "." + k

        if isinstance(v, dict):
            flattened_val = flatten(data=v, prev_key=newkey, level=level + 1)
            if newkey in use_keys:
                res.update(flattened_val)
            else:
                res.update({".".join(newkey.split(".")[level-2:]): flattened_val})

        else:
            if newkey.split(".")[-2] in use_keys:
                res.update({".".join(newkey.split(".")[level-1:]): v})
            else:
                res.update({k: v})
    return res

【问题讨论】:

    标签: python python-3.x dictionary object flatten


    【解决方案1】:

    您可以将递归与生成器一起使用:

    [data] = [{'CreatedBy': {'Name': 'User001'}, 'Lookup': {'TextField': 'Some text', 'UserField': {'Id': 'ID001', 'Name': 'Name001'}, 'CreatedBy': {'Name': 'User001'}}, 'Image': {'a': 'b'}}]
    def flatten_dict(d, use_keys = []):
      def new_lookup(_d, c = []):
         for a, b in _d.items():
            if not isinstance(b, dict):
               yield c+[a, b]
            else:
               yield from new_lookup(b, c + [a])
      def flatten(_d, c = []):
         new_d = {}
         for a, b in _d.items():
           if any((c+[a])[-len(i.split('.')):] == i.split('.') for i in use_keys):
              for *j, k in new_lookup(b):              
                new_d['.'.join([a,*j])] = k
           else:
              new_d[a] = b if not isinstance(b, dict) else flatten(b, c + [a])
         return new_d
      return flatten(d)
    
    #test case 1
    print([flatten_dict(data, use_keys = ["Image", "Lookup.CreatedBy", "CreatedBy"])])
    

    输出:

    [
      {'CreatedBy.Name': 'User001', 
       'Lookup': 
          {'TextField': 'Some text', 
          'UserField': {'Id': 'ID001', 'Name': 'Name001'}, 
          'CreatedBy.Name': 'User001'}, 
        'Image.a': 'b'}
    ]
    

    #test case 2
    print([flatten_dict(data, use_keys=["Image", "Lookup.CreatedBy"])])
    

    输出:

    [
       {'CreatedBy': {'Name': 'User001'}, 
       'Lookup': {'TextField': 'Some text', 
       'UserField': {'Id': 'ID001', 'Name': 'Name001'}, 
       'CreatedBy.Name': 'User001'}, 
       'Image.a': 'b'}
    ]
    

    #test case 3
    print([flatten_dict(data, use_keys=["Image", "Lookup.CreatedBy", "Lookup"])])
    

    输出:

    [
       {'CreatedBy': {'Name': 'User001'}, 
       'Lookup.TextField': 'Some text', 
       'Lookup.UserField.Id': 'ID001', 
       'Lookup.UserField.Name': 'Name001', 
       'Lookup.CreatedBy.Name': 'User001', 
       'Image.a': 'b'}
    ]
    

    【讨论】:

    • 嘿,感谢它完美运行的代码,我只是有一个疑问。 new_lookup 中的 else 部分在上述任何情况下都没有使用它的用途是什么
    • @BhavaniRavi new_lookup 由函数 flatten 调用。反过来,flattenflatten_dict 的主体中被调用。
    猜你喜欢
    • 2018-04-18
    • 2019-11-14
    • 2018-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-18
    • 1970-01-01
    相关资源
    最近更新 更多