【问题标题】:Merging multiple dictionaries with inconsistent keys合并具有不一致键的多个字典
【发布时间】:2019-07-23 20:00:55
【问题描述】:

我是一名 Python 初学者,正在努力解决以下问题:

我正在尝试将多个列表与我从多个 json 解码的嵌套字典合并。列表之间的共同点是每个嵌套字典对应一个名称的“uid”键,但问题是某些字典的键名不同。例如,一个字典可能有“数字”作为键,而不是“uid”。我想将它们的一部分合并到一个超级嵌套字典列表中。为了说明,我所拥有的是:

masterlist = [ ]

listA = [{"uid": "12345", "name": "John Smith"}, {etc...}]

listB = [{"number": "12345", "person": "John Smith", "val1": "25"}, {etc...}]

listC = [{"number": "12345", "person": "John Smith", "val2": "65"}, {etc...}]

我想结束的是:

masterlist = [{"uid": "12345", "name": "John Smith", "val1": "25", "val2: "65"}, {etc...}]

这是否可以通过迭代和比较相同的“uid”值来有效地/以python方式完成?我已经看到很多关于通过匹配键进行合并的方法,但这里的问题显然是键不一致。排序无关紧要。我所需要的只是让主列表包含每个 dict 条目的相应 uid、名称和值。希望这是有道理的,谢谢!

【问题讨论】:

  • 我应该澄清一下,如上所示,我可以保留 listA 的键:值对。因此,从这个意义上说,我要问的是如何将 listA 中的每个嵌套字典附加到 listB 中的 val1 和 listC 中的 val2 中,根据常见的“uid/number”值进行匹配。

标签: python json list dictionary merge


【解决方案1】:

您可以在没有 Pandas 的情况下使用列表解析来执行此操作,该列表理解构建字典字典以按其“uid”对列表的字典进行分组。然后,您可以使用该分组字典的 .values() 再次获取字典列表:

listA = [{"uid": "12345", "name": "John Smith"},{"uid": "67890", "name": "Jane Doe"}]

listB = [{"number": "12345", "person": "John Smith", "val1": "25"},{"number": "67890", "val1": "37"}]

listC = [{"number": "12345", "person": "John Smith", "val2": "65"},{"number": "67890", "val2": "53"}]

from collections import defaultdict
fn     = { "number":"uid", "person":"name" } # map to get uniform key names
data   = [ { fn.get(k,k):v for k,v in d.items() } for d in listA+listB+listC ]
result = next(r for r in [defaultdict(dict)] if [r[d["uid"]].update(d) for d in data])
print(*result.values())

{'uid': '12345', 'name': 'John Smith', 'val1': '25', 'val2': '65'} 
{'uid': '67890', 'name': 'Jane Doe', 'val1': '37', 'val2': '53'}

【讨论】:

    【解决方案2】:

    您应该将所有输入列表放入列表列表中,这样您就可以构造一个将 uid 映射到具有聚合项目值的 dict 的 dict,这样您所需的 dict 列表就是映射。为了允许不同输入字典中键的命名不一致,pop 是您不想要的(例如我的示例中的numberid)并使用您要保留的键分配给字典(如示例中的uid):

    wanted_key = 'uid'
    unwanted_keys = {'number', 'id'}
    mapping = {}
    for l in lists:
        for d in l:
            if wanted_key not in d:
                d[wanted_key] = d.pop(unwanted_keys.intersection(d).pop())
            mapping.setdefault(d[wanted_key], {}).update(d)
    masterlist = list(mapping.values())
    

    所以给定:

    lists = [
        [
            {"uid": "12345", "name": "John Smith"},
            {"uid": "56789", "name": "Joe Brown", "val1": "1"}
        ],
        [
            {"number": "12345", "name": "John Smith", "val1": "25"},
            {"number": "56789", "name": "Joe Brown", "val2": "2"}
        ],
        [
            {"id": "12345", "name": "John Smith", "val2": "65"}
        ]
    ]
    

    masterlist 变为:

    [
        {'uid': '12345', 'name': 'John Smith', 'val1': '25', 'val2': '65'},
        {'uid': '56789', 'name': 'Joe Brown', 'val1': '1', 'val2': '2'}
    ]
    

    【讨论】:

    • 如果“uid”键名在所有列表中不一致,这会起作用吗?就我而言,值“12345”在所有列表中都是一致的,但键具有不同的命名约定(“uid”、“name”等)。
    【解决方案3】:

    如果您需要为每个列表使用不同的键,这里有一个解决方案,它也使用中间 dict,其函数采用表示 uid 的键和一个或多个键来复制:

    people_by_uid = {person["uid"]: person for person in listA}
    
    def update_values(listX, uid_key, *val_keys):
        for entry in listX:
            person = people_by_uid[entry[uid_key]]
            for val_key in val_keys:
                person[val_key] = entry[val_key]
    
    update_values(listB, "number", "val1")
    update_values(listC, "number", "val2")
    
    # e.g. if you had a listD from which you also needed val3 and val4:
    update_values(listD, "number", "val3", "val4")
    
    masterlist = [person for person in people_by_uid.values()]
    

    【讨论】:

      【解决方案4】:

      可能有使用基本 python 的解决方案,但我能想到的最简单的方法是使用 pandas 库将每个列表转换为 DataFrame,然后将它们连接/合并在一起。

      import pandas as pd
      
      dfA = pd.DataFrame(listA)
      dfB = pd.DataFrame(listB)
      
      merged_df = dfA.merge(dfB, left_on='uid', right_on='number')
      

      这将返回一个包含比您需要的更多列的 DataFrame(即“uid”和“number”列),但您可以通过这种方式指定您想要的列以及您想要它们的顺序:

      merged_df = merged_df[['uid', 'name', 'val1']]
      

      关于将多个 DataFrame 合并为一个主框架,请参见此处:pandas three-way joining multiple dataframes on columns

      【讨论】:

      • 太棒了,这让我到达了我需要去的地方。谢谢!
      猜你喜欢
      • 2019-09-18
      • 2023-03-16
      • 1970-01-01
      • 2017-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-14
      • 1970-01-01
      相关资源
      最近更新 更多