【问题标题】:How to append a new value to the same key in the list of dictionaries?如何将新值附加到字典列表中的同一键?
【发布时间】:2020-03-01 08:06:43
【问题描述】:

我有一个测试失败列表,如下所示 -

all_failures = [
            'test1/path/to/test1/log/failure_reason1',
            'test1/path/to/test1/log/failure_reason2',
            'test2/path/to/test2/log/failure_reason1',
            'test2/path/to/test2/log/failure_reason2',
            'test3/path/to/test3/log/failure_reason1',
            'test4/path/to/test4/log/failure_reason1',
        ]

我试图通过解析列表中的每个失败来构造一个类似 JSON 的对象。到目前为止,我已经尝试编写以下代码 -

for failure in all_failures:
    data = failure.split('/',1)
    test = data[0]
    failure_details_dict[test] = []

    data = '/' + data[1]
    data = data.rsplit('/', 1)

    test_details_dict['path'] = data[0] + '/'
    test_details_dict['reason'] = data[1]

    failure_details_dict[test].append(test_details_dict)

    test_details_dict = {}  

for key,value in failure_details_dict.items():
    print(key)
    print(value)
    print()

我得到的输出是 -

test4
[{'reason': 'failure_reason1', 'path': '/path/to/test4/log/'}]

test3
[{'reason': 'failure_reason1', 'path': '/path/to/test3/log/'}]

test1
[{'reason': 'failure_reason2', 'path': '/path/to/test1/log/'}]

test2
[{'reason': 'failure_reason2', 'path': '/path/to/test2/log/'}]

然而,预期输出是 -

{
    "test1": [
                {
                    "path": "/path/to/test1/log/",
                    "reason": "failure_reason1" 
                },
                {
                    "path": "/path/to/test1/log/",
                    "reason": "failure_reason2"     
                }

            ],
    "test2": [
                {
                    "path": "/path/to/test2/log/",
                    "reason": "failure_reason1" 
                },
                {
                    "path": "/path/to/test2/log/",
                    "reason": "failure_reason2"     
                }
            ],
    "test3": [
                {
                    "path": "/path/to/test3/log/",
                    "reason": "failure_reason1" 
                },
            ],
    "test4": [
                {
                    "path": "/path/to/test4/log/",
                    "reason": "reason1" 
                },
            ]
}

正如我们所见,我无法将 second pathreason for failure 添加到同一个键中。示例 - test1 和 test2 有两个失败的原因。

有人可以帮助了解我缺少什么吗?谢谢!

【问题讨论】:

    标签: python json


    【解决方案1】:

    原因

    对于每个循环,您都将覆盖到 failure_details_dict[test]


    解决方案

    你应该只给它设置一次列表。
    您有多种选择。

    • 非pythonic方式(不推荐
    if test not in failure_details_dict:
        failure_details_dict[test] = []
    
    • 将分配替换为dict.setdefault 调用。这种方式不会影响与failure_details_dict 的其他交互
    failure_details_dict.setdefault(test, [])  # instead of failure_details_dict[test] = []
    
    • 使用collections.defaultdict 代替dict。这种方式会影响failure_detilas_dict的其他互动。
    from collections import defaultdict
    
    failure_details_dict = defaultdict(list)  # instead of {}
    

    示例

    我已经重构了你的代码:

    all_failures = [
        'test1/path/to/test1/log/failure_reason1',
        'test1/path/to/test1/log/failure_reason2',
        'test2/path/to/test2/log/failure_reason1',
        'test2/path/to/test2/log/failure_reason2',
        'test3/path/to/test3/log/failure_reason1',
        'test4/path/to/test4/log/failure_reason1',
    ]
    
    failure_details_dict = {}
    
    for failure in all_failures:
        key, *paths, reason = failure.split('/')
        failure_details_dict.setdefault(key, []).append({
            'path': f"/{'/'.join(paths)}/",
            'reason': reason,
        })
    
    for key, value in failure_details_dict.items():
        print(key)
        print(value)
        print()
    

    结论

    • 如果您想进行简单的更改,请使用dict.setdefault 方法。
    • 如果您对failure_details_dict 有多个访问权限,并且您希望每个访问权限都使用默认值,请使用collection.defaultdict 类。

    额外

    我们如何修改代码,使“path”键只复制一次,并且只创建多个带有“reason”键的字典?一般来说,以 JSON 格式存储数据的最佳方式是什么?

    您可以像这样重新格式化 JSON:

    {
      "test1": {
        "path": "/path/to/test1/log/",
        "reason": [
          "failure_reason1",
          "failure_reason2"
        ]
      },
      "test2": {
        "path": "/path/to/test2/log/",
        "reason": [
          "failure_reason1",
          "failure_reason2"
        ]
      },
      "test3": {
        "path": "/path/to/test3/log/",
        "reason": [
          "failure_reason1"
        ]
      },
      "test4": {
        "path": "/path/to/test4/log/",
        "reason": [
          "reason1"
        ]
      }
    }
    

    来自代码:

    all_failures = [
        'test1/path/to/test1/log/failure_reason1',
        'test1/path/to/test1/log/failure_reason2',
        'test2/path/to/test2/log/failure_reason1',
        'test2/path/to/test2/log/failure_reason2',
        'test3/path/to/test3/log/failure_reason1',
        'test4/path/to/test4/log/failure_reason1',
    ]
    
    failure_details_dict = {}
    
    for failure in all_failures:
        key, *paths, reason = failure.split('/')
        failure_details_dict.setdefault(key, {
            'path': f"/{'/'.join(paths)}/",
            'reason': [],
        })['reason'].append(reason)
    
    for key, value in failure_details_dict.items():
        print(key)
        print(value)
        print()
    

    【讨论】:

    • 感谢您的解决方案。我们如何修改代码以便只复制一次“路径”键并且只创建多个带有“原因”键的字典?一般来说,以 JSON 格式存储数据的最佳方式是什么?谢谢。
    【解决方案2】:

    您可以使用正则表达式从故障日志文件名中提取信息。这可以通过以下方式简单地实现:

    import re
    import json
    
    all_failures = [
                'test1/path/to/test1/log/failure_reason1',
                'test1/path/to/test1/log/failure_reason2',
                'test2/path/to/test2/log/failure_reason1',
                'test2/path/to/test2/log/failure_reason2',
                'test3/path/to/test3/log/failure_reason1',
                'test4/path/to/test4/log/failure_reason1',
            ]
    
    
    info = dict()
    for failure in all_failures:
        match = re.search(r"^(.*?)(/.*/)(.*)$", failure)
    
        details = dict()
        details["path"] = match.group(2)
        details["reason"] = match.group(3)
    
        if match.group(1) in info:
            info[match.group(1)].append(details)
        else:
            info[match.group(1)] = []
            info[match.group(1)].append(details)
    
    print(json.dumps(info, indent=4))
    

    输出:

    {
        "test1": [
            {
                "path": "/path/to/test1/log/",
                "reason": "failure_reason1"
            },
            {
                "path": "/path/to/test1/log/",
                "reason": "failure_reason2"
            }
        ],
        "test2": [
            {
                "path": "/path/to/test2/log/",
                "reason": "failure_reason1"
            },
            {
                "path": "/path/to/test2/log/",
                "reason": "failure_reason2"
            }
        ],
        "test3": [
            {
                "path": "/path/to/test3/log/",
                "reason": "failure_reason1"
            }
        ],
        "test4": [
            {
                "path": "/path/to/test4/log/",
                "reason": "failure_reason1"
            }
        ]
    }
    

    【讨论】:

    • 您可以使用 re.fullmatch 代替 re.search 与锚点(^, $
    • @Shubham - 感谢您的解决方案。我们如何修改代码,以便只复制一次“路径”键,并且只创建多个带有“原因”键的字典?一般来说,以 JSON 格式存储数据的最佳方式是什么?谢谢。
    猜你喜欢
    • 2020-04-09
    • 2022-01-06
    • 2023-03-23
    • 2015-07-03
    • 1970-01-01
    • 2022-12-17
    • 1970-01-01
    • 2021-11-14
    相关资源
    最近更新 更多