合并几个python词典

时间:2012-02-23 15:18:38

标签: python dictionary merge

我必须合并python字典列表。例如:

dicts[0] = {'a':1, 'b':2, 'c':3}
dicts[1] = {'a':1, 'd':2, 'c':'foo'}
dicts[2] = {'e':57,'c':3}

super_dict = {'a':[1], 'b':[2], 'c':[3,'foo'], 'd':[2], 'e':[57]}    

我写了以下代码:

super_dict = {}
for d in dicts:
    for k, v in d.items():
        if super_dict.get(k) is None:
            super_dict[k] = []
        if v not in super_dict.get(k):
            super_dict[k].append(v)

可以更优雅/优化地展示吗?

注意 我在SO上发现了另一个question,但它正在合并两个字典。

15 个答案:

答案 0 :(得分:30)

您可以直接迭代字典 - 无需使用range。 dict的setdefault方法查找键,如果找到则返回值。如果未找到,则返回默认值,并将该默认值分配给键。

super_dict = {}
for d in dicts:
    for k, v in d.iteritems():  # d.items() in Python 3+
        super_dict.setdefault(k, []).append(v)

另外,您可以考虑使用defaultdict。这只是通过调用函数来自动化setdefault,以便在找不到密钥时返回默认值。

import collections
super_dict = collections.defaultdict(list)
for d in dicts:
    for k, v in d.iteritems():  # d.items() in Python 3+
        super_dict[k].append(v)

此外,正如Sven Marnach精明地观察到的那样,您似乎不想在列表中重复值。在这种情况下,set可以获得您想要的内容:

import collections
super_dict = collections.defaultdict(set)
for d in dicts:
    for k, v in d.iteritems():  # d.items() in Python 3+
        super_dict[k].add(v)

答案 1 :(得分:23)

from collections import defaultdict

dicts = [{'a':1, 'b':2, 'c':3},
         {'a':1, 'd':2, 'c':'foo'},
         {'e':57, 'c':3} ]

super_dict = defaultdict(set)  # uses set to avoid duplicates

for d in dicts:
    for k, v in d.items():  # use d.iteritems() in python 2
        super_dict[k].add(v)

答案 2 :(得分:12)

合并所有dicts的键,并为每个键组合值列表:

super_dict = {}
for k in set(k for d in dicts for k in d):
    super_dict[k] = [d[k] for d in dicts if k in d]

表达式set(k for d in dicts for k in d)构建所有词典的所有唯一键的集合。对于这些唯一键中的每一个,我们使用列表推导[d[k] for d in dicts if k in d]来构建此键的所有词组的值列表。

由于您似乎只是每个键的唯一值,因此您可能希望使用集合:

super_dict = {}
for k in set(k for d in dicts for k in d):
    super_dict[k] = set(d[k] for d in dicts if k in d)

答案 3 :(得分:4)

当键的值在列表中时:

from collections import defaultdict

    dicts = [{'a':[1], 'b':[2], 'c':[3]},
             {'a':[11], 'd':[2], 'c':['foo']},
             {'e':[57], 'c':[3], "a": [1]} ]

super_dict = defaultdict(list)  # uses set to avoid duplicates

for d in dicts:
    for k, v in d.items():  # use d.iteritems() in python 2
        super_dict[k] = list(set(super_dict[k] + v))

combined_dict = {}

for elem in super_dict.keys():
    combined_dict[elem] = super_dict[elem]

combined_dict
## output: {'a': [1, 11], 'b': [2], 'c': [3, 'foo'], 'd': [2], 'e': [57]}

答案 4 :(得分:3)

永远不要忘记标准库有大量的工具来处理dicts和迭代:

from itertools import chain
from collections import defaultdict
super_dict = defaultdict(list)
for k,v in chain.from_iterable(d.iteritems() for d in dicts):
    if v not in super_dict[k]: super_dict[k].append(v)

请注意,根据Steven Rumbalski的回答,使用if v not in super_dict[k]可以避免defaultdict(set)

答案 5 :(得分:2)

这可能更优雅:

super_dict = {}
for d in dicts:
    for k, v in d.iteritems():
        l=super_dict.setdefault(k,[])
        if v not in l:
            l.append(v)

更新:由Sven提出改变

更新:改为避免重复(感谢Marcin和Steven)

答案 6 :(得分:2)

我有一个非常容易解决的解决方案,无需任何导入。 我使用dict.update()方法。 但是遗憾的是,如果同一关键字出现在多个词典中,它将覆盖,那么最近合并的dict的值将出现在输出中。

dict1 = {'Name': 'Zara', 'Age': 7}
dict2 = {'Sex': 'female' }
dict3 = {'Status': 'single', 'Age': 27}
dict4 = {'Occupation':'nurse', 'Wage': 3000}

def mergedict(*args):
    output = {}
    for arg in args:
        output.update(arg)
    return output
    
print(mergedict(dict1, dict2, dict3, dict4))

输出是这样的:

{'名称':'扎拉','年龄':27,'性别':'女性','状态':'单身','职业':'护士','工资':3000}

答案 7 :(得分:1)

对于oneliner,可以使用以下内容:

{key: {d[key] for d in dicts if key in d} for key in {key for d in dicts for key in d}}

虽然可读性将受益于命名组合密钥集:

combined_key_set = {key for d in dicts for key in d}
super_dict = {key: {d[key] for d in dicts if key in d} for key in combined_key_set}

优雅可以辩论,但我个人更喜欢理解而不是循环。 :)

(字典和集合理解在Python 2.7/3.1及更新版本中可用。)

答案 8 :(得分:1)

恐怕还没有人发表。.

d = {**one, **two, **three, **four}
print d

这就足够了。

答案 9 :(得分:0)

我的解决方案类似于@senderle提议的,但是我使用map

而不是for循环
super_dict = defaultdict(set)
map(lambda y: map(lambda x: super_dict[x].add(y[x]), y), dicts)

答案 10 :(得分:0)

如果您假设感兴趣的键处于同一嵌套级别,则可以递归地遍历每个字典并使用该键创建新字典,从而有效地合并它们。

merged = {}
for d in dicts:
    def walk(d,merge):
        for key, item in d.items():
            if isinstance(item, dict):
                merge.setdefault(key, {})
                walk(item, merge[key])
            else:
                merge.setdefault(key, [])
                merge[key].append(item)
    walk(d,merged)

例如,假设您要合并以下字典。

dicts = [{'A': {'A1': {'FOO': [1,2,3]}}},
         {'A': {'A1': {'A2': {'BOO': [4,5,6]}}}},
         {'A': {'A1': {'FOO': [7,8]}}},
         {'B': {'B1': {'COO': [9]}}},
         {'B': {'B2': {'DOO': [10,11,12]}}},
         {'C': {'C1': {'C2': {'POO':[13,14,15]}}}},
         {'C': {'C1': {'ROO': [16,17]}}}]

在每个级别使用密钥,您应该会得到类似以下内容的信息:

{'A': {'A1': {'FOO': [[1, 2, 3], [7, 8]], 
              'A2': {'BOO': [[4, 5, 6]]}}},
 'B': {'B1': {'COO': [[9]]}, 
       'B2': {'DOO': [[10, 11, 12]]}},
 'C': {'C1': {'C2': {'POO': [[13, 14, 15]]}, 
              'ROO': [[16, 17]]}}}

注意:我假设每个分支的叶子都是某种列表,但是显然您可以更改逻辑以执行您的情况所需的任何事情。

答案 11 :(得分:0)

使用defaultdict很好,也可以使用itertools.groupby来完成。

import itertools
# output all dict items, and sort them by key
dicts_ele = sorted( ( item for d in dicts for item in d.items() ), key = lambda x: x[0] )
# groups items by key
ele_groups = itertools.groupby( dicts_ele, key = lambda x: x[0] )
# iterates over groups and get item value
merged = { k: set( v[1] for v in grouped ) for k, grouped in ele_groups }

显然,您可以将此代码块合并为单行样式

merged = {
    k: set( v[1] for v in grouped )
    for k, grouped in (
        itertools.groupby(
            sorted(
                ( item for d in dicts for item in d.items() ),
                key = lambda x: x[0]
            ),
            key = lambda x: x[0]
        )
    )
}

答案 12 :(得分:0)

python 3.x (reduce是python 2.x的内置功能,因此在2.x版本中无需导入)

import operator
from functools import operator.add

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

dict(reduce(operator.add, map(list,(map(dict.items, a))))

map(dict.items, a)#转换为键值迭代器列表

map(list, ...#转换为等效于[[[[a,1]],[[b,2]],[[c,3],[d,4]]]的迭代器

reduce(operator.add, ...#将多个列表缩减为单个列表

答案 13 :(得分:-1)

似乎大多数使用理解的答案都不是那么可读。如果在上面的答案混乱中丢失,这可能会有所帮助(尽管非常晚......)。只需循环遍历每个字典的项目并将它们放在一个单独的字典中。

super_dict = {key:val for d in dicts for key,val in d.items()}

答案 14 :(得分:-2)

我在游戏中有点迟了但是我在2行中完成了它,除了python之外没有任何依赖:

flatten = lambda *c: (b for a in c for b in (flatten(*a) if isinstance(a, (tuple, list)) else (a,)))
o = reduce(lambda d1,d2: dict((k, list(flatten([d1.get(k), d2.get(k)]))) for k in set(d1.keys() + d2.keys())), dicts)
# output:
# {'a': [1, 1, None], 'c': [3, 'foo', 3], 'b': [2, None, None], 'e': [None, 57], 'd': [None, 2, None]}

虽然如果你不关心嵌套列表,那么:

o2 = reduce(lambda d1,d2: dict((k, [d1.get(k), d2.get(k)]) for k in set(d1.keys() + d2.keys())), dicts)
# output:
# {'a': [[1, 1], None], 'c': [[3, 'foo'], 3], 'b': [[2, None], None], 'e': [None, 57], 'd': [[None, 2], None]}