合并字典时保留重复键的值

8 投票
2 回答
17547 浏览
提问于 2025-04-18 13:18

给定n个字典,写一个函数,返回一个独特的字典,对于重复的键,值用列表的形式表示。

举个例子:

d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'b': 4}
d3 = {'a': 5, 'd': 6}

结果:

>>> newdict
{'c': 3, 'd': 6, 'a': [1, 5], 'b': [2, 4]}

我目前的代码:

>>> def merge_dicts(*dicts):
...     x = []
...     for item in dicts:
...         x.append(item)
...     return x
...
>>> merge_dicts(d1, d2, d3)
[{'a': 1, 'b': 2}, {'c': 3, 'b': 4}, {'a': 5, 'd': 6}]

有什么好的方法可以生成一个新的字典,让那些重复的键对应的值变成一个列表吗?

2 个回答

12

Python 提供了一个简单又快速的解决方案:在 collections 模块中的 defaultdict。下面是文档中的一些例子:

使用 list 作为 default_factory,可以很方便地把一系列的键值对分组到一个字典中,每个键对应一个列表:

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
... d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', 1), ('yellow', [1, 3])]

当第一次遇到某个键时,如果这个键还不在字典里,Python 会自动用 default_factory 函数创建一个新的条目,这个函数会返回一个空列表。接着,list.append() 操作会把这个值添加到新创建的列表中。当再次遇到同样的键时,Python 会正常查找(返回该键对应的列表),然后 list.append() 操作会把另一个值添加到这个列表里。

在你的情况下,大致可以这样理解:

import collections

def merge_dicts(*dicts):
    res = collections.defaultdict(list)
    for d in dicts:
        for k, v in d.iteritems():
            res[k].append(v)
    return res

>>> merge_dicts(d1, d2, d3)
defaultdict(<type 'list'>, {'a': [1, 5], 'c': [3], 'b': [2, 4], 'd': [6]})
4
def merge_dicts(*dicts):
    d = {}
    for dict in dicts:
        for key in dict:
            try:
                d[key].append(dict[key])
            except KeyError:
                d[key] = [dict[key]]
    return d

这个返回的是:

{'a': [1, 5], 'b': [2, 4], 'c': [3], 'd': [6]}

这里和问题有一点小区别。这里所有的字典值都是列表。如果你不想让长度为1的列表也变成列表的话,可以在:

    for key in d:
        if len(d[key]) == 1:
            d[key] = d[key][0]

return d语句之前加上这一行。不过,我真的很难想象在什么情况下你会想要去掉这个列表。(想象一下你有列表作为值的情况;那么去掉这些项外面的列表就会导致一些模糊的情况。)

撰写回答