如何基于某个值合并两个字典列表

6

我有两个字典列表,假设如下:

a = [{'id': 1, 'name': 'a'}]
b = [{'id': 1, 'city': 'b'}]

我希望有一个列表,将这两个列表中拥有相同ID的所有字典合并。在这个例子中,我期望得到:
a = [{'id': 1, 'name': 'a', 'city': 'b'}]

除了嵌套的 for 循环,还有更简洁的方法吗?

谢谢。


如果两个字典具有相同的键(除了id键),应该发生什么? - Gilad Green
每个字典只有一个键?这些是展示的最佳示例吗?也许如果您实际展示了您编写的解决方案,一切都会变得更清晰。 - Booboo
4个回答

7
你可以使用另一个字典(或使用defaultdict使事情更简单)来跟踪这些id。然后在迭代时更新该字典中的项目。最终,该字典的值将包含你的列表。
from collections import defaultdict
d = defaultdict(dict)

a = [{'id': 1, 'name': 'a'}, {'id': 3, 'name': 'a'}]
b = [{'id': 1, 'city': 'b'}, {'id': 2, 'city': 'c'}, {'id': 3, 'city': 'd'}]

for item in a + b:
    d[item['id']].update(item)
list(d.values())

# [{'id': 1, 'name': 'a', 'city': 'b'},
#  {'id': 3, 'name': 'a', 'city': 'd'},
#  {'id': 2, 'city': 'c'}]

注意,这将覆盖除 id 以外的重复值 - 因此,如果您有两个具有id: 1和两个不同城市的对象,则只会得到最后一个城市。

1
太好了,谢谢!还有一件事,如果我想添加一个默认值,比如'NoName',如果其中一个键不存在呢?假设: a = [{'id': 1, 'name': 'a'}, {'id': 3, 'name': 'a'}, {'id': 4, 'name': 'a'}] b = [{'id': 1, 'city': 'b'}, {'id': 2, 'city': 'c'}, {'id': 3, 'city': 'd'}]如果我想要的列表是: [{'id': 1, 'name': 'a', 'city': 'b'}, {'id': 3, 'name': 'a', 'city': 'd'}, {'id': 4, 'name': 'a', 'city': 'NoName'}, {'id': 2, 'city': 'c', 'name': 'NoName'}]还有其他更好的方法吗?除了在字典中检查该键是否存在,如果不存在,则将其添加? - Javi DR
1
JaviDR - 我现在不在电脑前,正在用手机打字,所以无法轻易地贴出代码。但你可以使用类似于 defaultdict(lambda: {your defaults here}) 这样的代码行生成一个填充好默认值的字典。 - Mark

1
一种做法是创建一个字典,将你想要使用的标识符(在这种情况下是id)映射到合并结果的字典。
#!/usr/bin/python

import collections

def merge_on_key(list_of_dictionaries, key, result):
    for d in list_of_dictionaries:
        assert(key in d)
        result[d[key]].update(d)

a = [{'id': 1, 'name': 'a'}]
b = [{'id': 1, 'city': 'b'}, {'id': 2, 'color': 'blue'}]

print 'a', a
print 'b', b

c = collections.defaultdict(lambda: {})
merge_on_key(a, 'id', c)
merge_on_key(b, 'id', c)

print 'merged results in dictionary with id 1', c[1]

That returns:

merged results in dictionary with id 1 {'city': 'b', 'id': 1, 'name': 'a'}

问题描述有多个字典组成的列表,而不仅仅是两个字典。 - Gilad Green
1
糟糕!让我尝试一下,看看能否合理地进行更新。 - James Thompson

1
你可以使用maplambda函数与update方法一起操作字典,像这样:
a = [{'id': 1, 'name': 'a'}, {'id': 2, 'name': 'a'}, {'id': 3, 'name': 'k'}]
b = [{'id': 1, 'city': 'b'}, {'id': 2, 'city': 'c'},  {'id': 4, 'city': 'cm'}]
a.extend(list(map(lambda x,y: y if x.get('id') != y.get('id') else x.update(y), a, b)))
a = list(filter(None, a))

a现在将成为一个包含合并值字典的列表,如下所示:

[{'id': 1, 'name': 'a', 'city': 'b'},
 {'id': 2, 'name': 'a', 'city': 'c'},
 {'id': 3, 'name': 'k'},
 {'id': 4, 'city': 'cm'}]

为了让它工作,你必须假设 a[0].id == b[0].id,但这并不总是成立的。 - Pranav Hosangadi
另外,您介意添加一下您的代码是做什么的吗?请记住,您正在为广大受众编写答案,包括解释为什么这个解决方案有效会让您的答案更好。 - Pranav Hosangadi
我还希望它能够将b中存在但不在a中的任何项目添加到a中。例如: a = [{'id': 1, 'name': 'a'}] b = [{'id': 2, 'city': 'b'}, {'id': 1, 'city': 'c'}]应该得到a = [{'id': 1, 'name': 'a', 'city': 'c'}, {'id': 2, 'city': 'b'}]。 - Javi DR
好的,我已经将它添加到解决方案中了。 - Anup Tiwari

0
from collections import defaultdict
from operator import itemgetter
l1 =[{'id': 1, 'City': 'Calcutta'}, {'id': 3, 'Country': 'Germany'}]
l2 = [{'id': 1, 'Country': 'India'}, {'id': 2, 'City': 'Delhi'}, {'id': 3, 'City': 'Berlin'}]

def merge1(l1,l2):
    d = defaultdict(dict)
    for l in (l1, l2):
        for innerdict1 in l:
            d[innerdict1['id']].update(innerdict1)

    l4 = sorted(d.values(), key=itemgetter("id"))
    l4p = print(l4)
    return l4p
merge1(l1, l2)

"""
[{'id': 1, 'City': 'Delhi', 'Country': 'India'}, {'id': 2, 'City': 'Calcutta'}, {'id': 3, 'Country': 'Germany', 'City': 'Berlin'}]

"""

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接