【问题标题】:Python merge two lists of dicts, where dict key matchesPython合并两个dicts列表,其中dict键匹配
【发布时间】:2021-03-12 14:44:23
【问题描述】:

我有两个列表,每个列表包含约 100 个字典。此处显示了一个虚拟插图:

first = [
    {"ip-10-1-1-1": {"job": {"company": "IBM", "title": "engineer"}}},
    {"ip-10-1-1-20": {"job": {"company": "Dell", "title": "manager"}}},
    {"ip-10-1-1-35": {"job": {"company": "Apple", "title": "CEO"}}},
]

second = [
    {"ip-10-1-1-1": {"demographics": {"age": 30, "gender": "female"}}},
    {"ip-10-1-1-20": {"demographics": {"age": "30", "gender": "male"}}},
    {"ip-10-1-1-49": {"demographics": {"age": "32", "gender": "female"}}},
]

我正在尝试将这些与此结果合并:

[
    {
        "ip-10-1-1-1": {
            "demographics": {"age": 30, "gender": "female"},
            "job": {"company": "IBM", "title": "engineer"},
        }
    },
    {
        "ip-10-1-1-20": {
            "demographics": {"age": "30", "gender": "male"},
            "job": {"company": "Dell", "title": "manager"},
        }
    },
    {"ip-10-1-1-35": {"job": {"company": "Apple", "title": "CEO"}}},
    {"ip-10-1-1-49": {"demographics": {"age": "32", "gender": "female"}}},
]


我几乎可以通过循环遍历secondfirst 来实现这一点,如下所示:

merged = []
for d1 in second:
    for k1 in d1.keys():
        for d2 in first:
            for k2 in d2.keys():
                if k2 == k1:
                    d1[k1]["job"] = d2[k2]["job"]
    merged.append(d1)
print(merged)

不过,我是 python 新手,我希望/认为必须有一种更 Python 的方式来做到这一点。

编辑:更复杂的事情-我可以在first 中拥有密钥,但在second 中没有,反之亦然。我已更新示例以反映这一点。

【问题讨论】:

  • 请发布一个更新的问题(带有您感兴趣的输入/输出示例)
  • 任一列表是否可以包含多个具有相同 top-level 键的字典?例如[{'u1':,,,}, {'u2':...}, {'u1':...}]
  • @RiccardoBucco 这不是我在上面所做的吗?我展示了这两个列表和我想要的结果,以及一个我有但不喜欢的丑陋解决方案......
  • 好吧,我为您发布的示例提供了一个解决方案 :) 但您不喜欢它,因为您提供给我们的密钥不正确。好吧,那么请用您正在谈论的键更新您的示例,我会相应地更改我的答案
  • 一个非常难看的解决方案,而不是 pythonic [{list(i[0].keys())[0]:{l:k[l] for j in i for k in j.values() for l in k}} for i in zip(first, second)]

标签: python


【解决方案1】:

这是一种可能的单行解决方案:

result = {f'user_{i + 1}': dict(**d1[f'user_{i + 1}'], **d2[f'user_{i + 1}'])
          for i, (d1, d2) in enumerate(zip(first, second))}

如果您使用的是 python >=3.8,那么您可以在 dict 理解中使用赋值:

result = {(key := f'user_{i + 1}'): dict(**d1[key], **d2[key])
          for i, (d1, d2) in enumerate(zip(first, second))}

【讨论】:

  • 我应该澄清一下 - 实际上我将处理 UUID 或可能的 IP 地址,而不是 user_1user_2
【解决方案2】:
first = [
    {"user_1": {"job": {"company": "IBM", "title": "engineer"}}},
    {"user_2": {"job": {"company": "Dell", "title": "manager"}}},
    {"user_3": {"job": {"company": "Microsoft", "title": "manager"}}},
]
second = [
    {"user_2": {"demographics": {"age": "30", "gender": "male"}}},
    {"user_1": {"demographics": {"age": "30", "gender": "female"}}},
]


def merge_list_of_dicts(list_of_dicts, current={}):
    get_key = lambda d: next(iter(d))
    get_value = lambda d: next(iter(d.values()))

    for d in list_of_dicts:
        key = get_key(d)
        value = get_value(d)

        if key not in current:
            current[key] = value
        else:
            current[key].update(value)

    return current


output = merge_list_of_dicts(second, merge_list_of_dicts(first))
print(output)

将处理first 中的额外键以及乱序字典(注意上面的firstsecond 列表)。输出:

{
    "user_1": {
        "job": {"company": "IBM", "title": "engineer"},
        "demographics": {"age": "30", "gender": "female"},
    },
    "user_2": {
        "job": {"company": "Dell", "title": "manager"},
        "demographics": {"age": "30", "gender": "male"},
    },
    "user_3": {"job": {"company": "Microsoft", "title": "manager"}},
}

如果您想要更简洁的 API:

def merge_list_of_dicts(d1: dict, d2: dict):
    def merge(list_of_dicts, current={}):
        get_key = lambda d: next(iter(d))
        get_value = lambda d: next(iter(d.values()))

        for d in list_of_dicts:
            key = get_key(d)
            value = get_value(d)

            if key not in current:
                current[key] = value
            else:
                current[key].update(value)

        return current
    return merge(d2, merge(d1))


output = merge_list_of_dicts(first, second)

【讨论】:

  • 这正是我想要完成的。谢谢!
  • 是的,没问题。
【解决方案3】:
import itertools

final_list = []
for key, group in itertools.groupby(
    sorted(first + second, key=lambda x: tuple(x)[0]), key=lambda x: tuple(x)[0]
):
    temp = {key: {}}
    for d in group:
        _, value = tuple(*d.items())
        temp[key].update(value)
    final_list.append(temp)
print(final_list)

输出:

[{'ip-10-1-1-1': {'job': {'company': 'IBM', 'title': 'engineer'}, 'demographics': {'age': 30, 'gender': 'female'}}}, {'ip-10-1-1-20': {'job': {'company': 'Dell', 'title': 'manager'}, 'demographics': {'age': '30', 'gender': 'male'}}}, {'ip-10-1-1-35': {'job': {'company': 'Apple', 'title': 'CEO'}}}, {'ip-10-1-1-49': {'demographics': {'age': '32', 'gender': 'female'}}}]

【讨论】:

  • 嗯,我想这让我很接近,谢谢。我也应该澄清一下,我可以在第一而不是第二,反之亦然。
猜你喜欢
  • 2020-10-12
  • 2021-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
  • 2015-04-12
相关资源
最近更新 更多