我需要合并多个词典,这就是我的例子:
dict1 = {1:{"a":{A}}, 2:{"b":{B}}}
dict2 = {2:{"c":{C}}, 3:{"d":{D}}
A
B
C
和D
是树的叶子,如{"info1":"value", "info2":"value2"}
词典的级别(深度)未知,可能是{2:{"c":{"z":{"y":{C}}}}}
在我的例子中,它代表一个目录/文件结构,其中节点是docs并且是文件。
我想将它们合并以获得:
dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}
我不确定如何使用Python轻松做到这一点。
答案 0 :(得分:111)
这实际上非常棘手 - 特别是如果您在事情不一致时需要有用的错误消息,同时正确接受重复但一致的条目(这里没有其他答案......)
假设您没有大量条目,递归函数最简单:
def merge(a, b, path=None):
"merges b into a"
if path is None: path = []
for key in b:
if key in a:
if isinstance(a[key], dict) and isinstance(b[key], dict):
merge(a[key], b[key], path + [str(key)])
elif a[key] == b[key]:
pass # same leaf value
else:
raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
else:
a[key] = b[key]
return a
# works
print(merge({1:{"a":"A"},2:{"b":"B"}}, {2:{"c":"C"},3:{"d":"D"}}))
# has conflict
merge({1:{"a":"A"},2:{"b":"B"}}, {1:{"a":"A"},2:{"b":"C"}})
请注意,这会改变a
- b
的内容会添加到a
(也会返回)。如果您想保留a
,可以将其称为merge(dict(a), b)
。
agf指出(下面)你可能有两个以上的词组,在这种情况下你可以使用:
reduce(merge, [dict1, dict2, dict3...])
将所有内容添加到dict1。
[注意 - 我编辑了我最初的答案来改变第一个论点;这使“减少”更容易解释]
在python 3中 ps,你还需要from functools import reduce
答案 1 :(得分:24)
使用生成器这是一种简单的方法:
def mergedicts(dict1, dict2):
for k in set(dict1.keys()).union(dict2.keys()):
if k in dict1 and k in dict2:
if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
yield (k, dict(mergedicts(dict1[k], dict2[k])))
else:
# If one of the values is not a dict, you can't continue merging it.
# Value from second dict overrides one in first and we move on.
yield (k, dict2[k])
# Alternatively, replace this with exception raiser to alert you of value conflicts
elif k in dict1:
yield (k, dict1[k])
else:
yield (k, dict2[k])
dict1 = {1:{"a":"A"},2:{"b":"B"}}
dict2 = {2:{"c":"C"},3:{"d":"D"}}
print dict(mergedicts(dict1,dict2))
打印:
{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}
答案 2 :(得分:18)
这个问题的一个问题是dict的值可以是任意复杂的数据。基于这些和其他答案,我想出了这段代码:
class YamlReaderError(Exception):
pass
def data_merge(a, b):
"""merges b into a and return merged result
NOTE: tuples and arbitrary objects are not handled as it is totally ambiguous what should happen"""
key = None
# ## debug output
# sys.stderr.write("DEBUG: %s to %s\n" %(b,a))
try:
if a is None or isinstance(a, str) or isinstance(a, unicode) or isinstance(a, int) or isinstance(a, long) or isinstance(a, float):
# border case for first run or if a is a primitive
a = b
elif isinstance(a, list):
# lists can be only appended
if isinstance(b, list):
# merge lists
a.extend(b)
else:
# append to list
a.append(b)
elif isinstance(a, dict):
# dicts must be merged
if isinstance(b, dict):
for key in b:
if key in a:
a[key] = data_merge(a[key], b[key])
else:
a[key] = b[key]
else:
raise YamlReaderError('Cannot merge non-dict "%s" into dict "%s"' % (b, a))
else:
raise YamlReaderError('NOT IMPLEMENTED "%s" into "%s"' % (b, a))
except TypeError, e:
raise YamlReaderError('TypeError "%s" in key "%s" when merging "%s" into "%s"' % (e, key, b, a))
return a
我的用例是merging YAML files,我只需要处理可能数据类型的子集。因此我可以忽略元组和其他对象。对我来说,合理的合并逻辑意味着
其他一切和不可预见的事都会导致错误。
答案 3 :(得分:10)
字典词典合并
由于这是规范问题(尽管存在某些非一般性),但我正在提供规范的Pythonic方法来解决这个问题。
d1 = {'a': {1: {'foo': {}}, 2: {}}}
d2 = {'a': {1: {}, 2: {'bar': {}}}}
d3 = {'b': {3: {'baz': {}}}}
d4 = {'a': {1: {'quux': {}}}}
这是最简单的递归案例,我建议采用两种天真的方法:
def rec_merge1(d1, d2):
'''return new merged dict of dicts'''
for k, v in d1.items(): # in Python 2, use .iteritems()!
if k in d2:
d2[k] = rec_merge1(v, d2[k])
d3 = d1.copy()
d3.update(d2)
return d3
def rec_merge2(d1, d2):
'''update first dict with second recursively'''
for k, v in d1.items(): # in Python 2, use .iteritems()!
if k in d2:
d2[k] = rec_merge2(v, d2[k])
d1.update(d2)
return d1
我相信我更喜欢第二个到第一个,但请记住,第一个的原始状态必须从其原点重建。这是用法:
>>> from functools import reduce # only required for Python 3.
>>> reduce(rec_merge1, (d1, d2, d3, d4))
{'a': {1: {'quux': {}, 'foo': {}}, 2: {'bar': {}}}, 'b': {3: {'baz': {}}}}
>>> reduce(rec_merge2, (d1, d2, d3, d4))
{'a': {1: {'quux': {}, 'foo': {}}, 2: {'bar': {}}}, 'b': {3: {'baz': {}}}}
因此,如果他们以dicts结尾,那么合并最终空的dicts就是一个简单的例子。如果没有,那不是那么微不足道。如果是字符串,你如何合并它们?集可以类似地更新,因此我们可以给予该处理,但是我们失去了它们被合并的顺序。订单也很重要吗?
因此,代替更多信息,最简单的方法是在两个值都不是dicts的情况下给它们标准的更新处理:即第二个dict的值将覆盖第一个,即使第二个dict的值为None且第一个价值是一个有很多信息的字典。
d1 = {'a': {1: 'foo', 2: None}}
d2 = {'a': {1: None, 2: 'bar'}}
d3 = {'b': {3: 'baz'}}
d4 = {'a': {1: 'quux'}}
from collections import MutableMapping
def rec_merge(d1, d2):
'''
Update two dicts of dicts recursively,
if either mapping has leaves that are non-dicts,
the second's leaf overwrites the first's.
'''
for k, v in d1.items(): # in Python 2, use .iteritems()!
if k in d2:
# this next check is the only difference!
if all(isinstance(e, MutableMapping) for e in (v, d2[k])):
d2[k] = rec_merge(v, d2[k])
# we could further check types and merge as appropriate here.
d3 = d1.copy()
d3.update(d2)
return d3
现在
from functools import reduce
reduce(rec_merge, (d1, d2, d3, d4))
返回
{'a': {1: 'quux', 2: 'bar'}, 'b': {3: 'baz'}}
我不得不删除字母周围的花括号,并将它们放在单引号中,这是合法的Python(否则它们将在Python 2.7+中设置文字)以及附加缺少的括号:
dict1 = {1:{"a":'A'}, 2:{"b":'B'}}
dict2 = {2:{"c":'C'}, 3:{"d":'D'}}
和rec_merge(dict1, dict2)
现在返回:
{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}
哪个与原始问题的预期结果相符(更改后,例如{A}
到'A'
。)
答案 4 :(得分:7)
基于@andrew cooke。此版本处理嵌套的dicts列表,并允许选项更新值
def merge(a, b, path=None, update=True): "http://stackoverflow.com/questions/7204805/python-dictionaries-of-dictionaries-merge" "merges b into a" if path is None: path = [] for key in b: if key in a: if isinstance(a[key], dict) and isinstance(b[key], dict): merge(a[key], b[key], path + [str(key)]) elif a[key] == b[key]: pass # same leaf value elif isinstance(a[key], list) and isinstance(b[key], list): for idx, val in enumerate(b[key]): a[key][idx] = merge(a[key][idx], b[key][idx], path + [str(key), str(idx)], update=update) elif update: a[key] = b[key] else: raise Exception('Conflict at %s' % '.'.join(path + [str(key)])) else: a[key] = b[key] return a
答案 5 :(得分:6)
如果你有一个未知级别的词典,那么我会建议一个递归函数:
def combineDicts(dictionary1, dictionary2):
output = {}
for item, value in dictionary1.iteritems():
if dictionary2.has_key(item):
if isinstance(dictionary2[item], dict):
output[item] = combineDicts(value, dictionary2.pop(item))
else:
output[item] = value
for item, value in dictionary2.iteritems():
output[item] = value
return output
答案 6 :(得分:4)
根据@andrew cooke的回答。 它以更好的方式处理嵌套列表。
def deep_merge_lists(original, incoming):
"""
Deep merge two lists. Modifies original.
Reursively call deep merge on each correlated element of list.
If item type in both elements are
a. dict: call deep_merge_dicts on both values.
b. list: Calls deep_merge_lists on both values.
c. any other type: Value is overridden.
d. conflicting types: Value is overridden.
If length of incoming list is more that of original then extra values are appended.
"""
common_length = min(len(original), len(incoming))
for idx in range(common_length):
if isinstance(original[idx], dict) and isinstance(incoming[idx], dict):
deep_merge_dicts(original[idx], incoming[idx])
elif isinstance(original[idx], list) and isinstance(incoming[idx], list):
deep_merge_lists(original[idx], incoming[idx])
else:
orginal[idx] = incoming[idx]
for idx in range(common_length, len(incoming)):
original.append(incoming[idx])
def deep_merge_dicts(original, incoming):
"""
Deep merge two dictionaries. Modfies original.
For key conflicts if both values are:
a. dict: Recursivley call deep_merge_dicts on both values.
b. list: Calls deep_merge_lists on both values.
c. any other type: Value is overridden.
d. conflicting types: Value is overridden.
"""
for key in incoming:
if key in original:
if isinstance(original[key], dict) and isinstance(incoming[key], dict):
deep_merge_dicts(original[key], incoming[key])
elif isinstance(original[key], list) and isinstance(incoming[key], list):
deep_merge_lists(original[key], incoming[key])
else:
original[key] = incoming[key]
else:
original[key] = incoming[key]
答案 7 :(得分:3)
这个简单的递归过程会将一个字典合并到另一个字典中,同时覆盖冲突的密钥:
#!/usr/bin/env python2.7
def merge_dicts(dict1, dict2):
""" Recursively merges dict2 into dict1 """
if not isinstance(dict1, dict) or not isinstance(dict2, dict):
return dict2
for k in dict2:
if k in dict1:
dict1[k] = merge_dicts(dict1[k], dict2[k])
else:
dict1[k] = dict2[k]
return dict1
print (merge_dicts({1:{"a":"A"}, 2:{"b":"B"}}, {2:{"c":"C"}, 3:{"d":"D"}}))
print (merge_dicts({1:{"a":"A"}, 2:{"b":"B"}}, {1:{"a":"A"}, 2:{"b":"C"}}))
输出:
{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}
{1: {'a': 'A'}, 2: {'b': 'C'}}
答案 8 :(得分:2)
<强>概述强>
以下方法将dicts深度合并的问题细分为:
使用a的参数化浅合并函数merge(f)(a,b)
函数f
合并两个词组a
和b
与f
一起使用的递归合并函数merge
<强>实施强>
用于合并两个(非嵌套)dicts的函数可以用很多方式编写。我个人喜欢
def merge(f):
def merge(a,b):
keys = a.keys() | b.keys()
return {key:f(*[a.get(key), b.get(key)]) for key in keys}
return merge
定义合适的recurrsive合并函数f
的好方法是使用multipledispatch,它允许定义根据参数类型在不同路径上计算的函数。
from multipledispatch import dispatch
#for anything that is not a dict return
@dispatch(object, object)
def f(a, b):
return b if b is not None else a
#for dicts recurse
@dispatch(dict, dict)
def f(a,b):
return merge(f)(a,b)
示例强>
要合并两个嵌套的词典,只需使用merge(f)
例如:
dict1 = {1:{"a":"A"},2:{"b":"B"}}
dict2 = {2:{"c":"C"},3:{"d":"D"}}
merge(f)(dict1, dict2)
#returns {1: {'a': 'A'}, 2: {'b': 'B', 'c': 'C'}, 3: {'d': 'D'}}
备注:强>
这种方法的优点是:
该函数是从较小的函数构建的,每个函数都做一件事 这使代码更容易推理和测试
行为不是硬编码的,但可以根据需要进行更改和扩展,从而提高代码重用率(请参阅下面的示例)。
<强>定制强>
有些答案还考虑了包含列表的词组,例如:其他(可能是嵌套的)dicts。在这种情况下,人们可能希望在列表上进行映射并根据位置合并它们。这可以通过向合并函数f
添加另一个定义来完成:
import itertools
@dispatch(list, list)
def f(a,b):
return [merge(f)(*arg) for arg in itertools.zip_longest(a,b,fillvalue={})]
答案 9 :(得分:2)
由于dictviews支持set操作,因此我能够大大简化jterrace的答案。
def merge(dict1, dict2):
for k in dict1.keys() - dict2.keys():
yield (k, dict1[k])
for k in dict2.keys() - dict1.keys():
yield (k, dict2[k])
for k in dict1.keys() & dict2.keys():
yield (k, dict(merge(dict1[k], dict2[k])))
任何将dict与非dict组合的尝试(技术上,具有&#39;键&#39;方法的对象和没有&#39;键&#39;方法的对象)都会引发AttributeError。这包括对函数的初始调用和递归调用。这正是我想要的,所以我离开了它。您可以轻松捕获递归调用抛出的AttributeErrors,然后产生您想要的任何值。
答案 10 :(得分:2)
安德鲁厨师回答:在某些情况下,当您修改返回的字典时,它会修改第二个参数b
。特别是因为这条线:
if key in a:
...
else:
a[key] = b[key]
如果b[key]
是dict
,则只会将其分配给a
,这意味着对dict
的任何后续修改都会影响a
和{ {1}}。
b
要解决此问题,该行必须替换为:
a={}
b={'1':{'2':'b'}}
c={'1':{'3':'c'}}
merge(merge(a,b), c) # {'1': {'3': 'c', '2': 'b'}}
a # {'1': {'3': 'c', '2': 'b'}} (as expected)
b # {'1': {'3': 'c', '2': 'b'}} <----
c # {'1': {'3': 'c'}} (unmodified)
if isinstance(b[key], dict):
a[key] = clone_dict(b[key])
else:
a[key] = b[key]
的位置:
clone_dict
静止。这显然不能解释def clone_dict(obj):
clone = {}
for key, value in obj.iteritems():
if isinstance(value, dict):
clone[key] = clone_dict(value)
else:
clone[key] = value
return
,list
和其他内容,但我希望它能说明尝试合并set
时的陷阱。
为了完整起见,这是我的版本,您可以在其中传递多个dicts
:
dicts
答案 11 :(得分:2)
您可以尝试mergedeep。
安装
$ pip3 install mergedeep
用法
from mergedeep import merge
a = {"keyA": 1}
b = {"keyB": {"sub1": 10}}
c = {"keyB": {"sub2": 20}}
merge(a, b, c)
print(a)
# {"keyA": 1, "keyB": {"sub1": 10, "sub2": 20}}
有关选项的完整列表,请查看docs!
答案 12 :(得分:2)
此版本的函数将考虑N个字典,并且只有字典 - 不能传递不正确的参数,否则会引发TypeError。合并本身解决了关键冲突,而不是从合并链中的字典中覆盖数据,而是创建一组值并附加到其中;没有数据丢失。
它可能不是页面上最有效的,但它是最彻底的,当你合并2到N个dicts时,你不会丢失任何信息。
def merge_dicts(*dicts):
if not reduce(lambda x, y: isinstance(y, dict) and x, dicts, True):
raise TypeError, "Object in *dicts not of type dict"
if len(dicts) < 2:
raise ValueError, "Requires 2 or more dict objects"
def merge(a, b):
for d in set(a.keys()).union(b.keys()):
if d in a and d in b:
if type(a[d]) == type(b[d]):
if not isinstance(a[d], dict):
ret = list({a[d], b[d]})
if len(ret) == 1: ret = ret[0]
yield (d, sorted(ret))
else:
yield (d, dict(merge(a[d], b[d])))
else:
raise TypeError, "Conflicting key:value type assignment"
elif d in a:
yield (d, a[d])
elif d in b:
yield (d, b[d])
else:
raise KeyError
return reduce(lambda x, y: dict(merge(x, y)), dicts[1:], dicts[0])
print merge_dicts({1:1,2:{1:2}},{1:2,2:{3:1}},{4:4})
输出:{1:[1,2],2:{1:2,3:1},4:4}
答案 13 :(得分:1)
短-甜:
from collections.abc import MutableMapping as Map
def nested_update(d, v):
"""
Nested update of dict-like 'd' with dict-like 'v'.
"""
for key in v:
if key in d and isinstance(d[key], Map) and isinstance(v[key], Map):
nested_update(d[key], v[key])
else:
d[key] = v[key]
这类似于(并基于)Python的dict.update
方法。它返回None
(如果愿意,您随时可以添加return d
),因为它就地更新了字典d
。 v
中的键将覆盖d
中的任何现有键(它不会尝试解释字典的内容)。
它也适用于其他(类似于“字典式”的)映射。
答案 14 :(得分:1)
import toolz
dict1={1:{"a":"A"},2:{"b":"B"}}
dict2={2:{"c":"C"},3:{"d":"D"}}
toolz.merge_with(toolz.merge,dict1,dict2)
给予
{1: {'a': 'A'}, 2: {'b': 'B', 'c': 'C'}, 3: {'d': 'D'}}
答案 15 :(得分:1)
我有一个迭代的解决方案-大字典及其很多(例如json等)的效果要好得多:
storeNewUser(FirebaseUser user, BuildContext context)
请注意,如果它们不是两个字典,则将使用d2中的值覆盖d1。 (与python的import collections
def merge_dict_with_subdicts(dict1: dict, dict2: dict) -> dict:
"""
similar behaviour to builtin dict.update - but knows how to handle nested dicts
"""
q = collections.deque([(dict1, dict2)])
while len(q) > 0:
d1, d2 = q.pop()
for k, v in d2.items():
if k in d1 and isinstance(d1[k], dict) and isinstance(v, dict):
q.append((d1[k], v))
else:
d1[k] = v
return dict1
相同)
一些测试
dict.update()
我测试了大约1200 dicts-这种方法花费了0.4秒,而递归解决方案花费了约2.5秒。
答案 16 :(得分:1)
我有两个词典(a
和b
),每个词典可以包含任意数量的嵌套词典。我希望以递归方式合并它们,b
优先于a
。
将嵌套字典视为树,我想要的是:
a
,以便b
中每个叶子的每个路径都显示在a
a
的相应路径中找到了一个叶子,则覆盖b
的子树
b
叶节点保持叶子的不变量。根据我的口味,现有的答案有点复杂,并在架子上留下了一些细节。我将以下内容混合在一起,它通过了我的数据集的单元测试。
def merge_map(a, b):
if not isinstance(a, dict) or not isinstance(b, dict):
return b
for key in b.keys():
a[key] = merge_map(a[key], b[key]) if key in a else b[key]
return a
示例(为清晰起见而格式化):
a = {
1 : {'a': 'red',
'b': {'blue': 'fish', 'yellow': 'bear' },
'c': { 'orange': 'dog'},
},
2 : {'d': 'green'},
3: 'e'
}
b = {
1 : {'b': 'white'},
2 : {'d': 'black'},
3: 'e'
}
>>> merge_map(a, b)
{1: {'a': 'red',
'b': 'white',
'c': {'orange': 'dog'},},
2: {'d': 'black'},
3: 'e'}
b
中需要维护的路径是:
1 -> 'b' -> 'white'
2 -> 'd' -> 'black'
3 -> 'e'
。 a
有唯一且无冲突的路径:
1 -> 'a' -> 'red'
1 -> 'c' -> 'orange' -> 'dog'
所以它们仍然在合并的地图中表示。
答案 17 :(得分:1)
这有助于将dict2
中的所有项目合并到dict1
:
for item in dict2:
if item in dict1:
for leaf in dict2[item]:
dict1[item][leaf] = dict2[item][leaf]
else:
dict1[item] = dict2[item]
请测试并告诉我们这是否是你想要的。
修改强>
上述解决方案仅合并一个级别,但正确地解决了OP给出的示例。要合并多个级别,应使用递归。
答案 18 :(得分:0)
class Utils(object):
"""
>>> a = { 'first' : { 'all_rows' : { 'pass' : 'dog', 'number' : '1' } } }
>>> b = { 'first' : { 'all_rows' : { 'fail' : 'cat', 'number' : '5' } } }
>>> Utils.merge_dict(b, a) == { 'first' : { 'all_rows' : { 'pass' : 'dog', 'fail' : 'cat', 'number' : '5' } } }
True
>>> main = {'a': {'b': {'test': 'bug'}, 'c': 'C'}}
>>> suply = {'a': {'b': 2, 'd': 'D', 'c': {'test': 'bug2'}}}
>>> Utils.merge_dict(main, suply) == {'a': {'b': {'test': 'bug'}, 'c': 'C', 'd': 'D'}}
True
"""
@staticmethod
def merge_dict(main, suply):
"""
获取融合的字典,以main为主,suply补充,冲突时以main为准
:return:
"""
for key, value in suply.items():
if key in main:
if isinstance(main[key], dict):
if isinstance(value, dict):
Utils.merge_dict(main[key], value)
else:
pass
else:
pass
else:
main[key] = value
return main
if __name__ == '__main__':
import doctest
doctest.testmod()
答案 19 :(得分:0)
我在这里有另一个稍微不同的解决方案:
def deepMerge(d1, d2, inconflict = lambda v1,v2 : v2) :
''' merge d2 into d1. using inconflict function to resolve the leaf conflicts '''
for k in d2:
if k in d1 :
if isinstance(d1[k], dict) and isinstance(d2[k], dict) :
deepMerge(d1[k], d2[k], inconflict)
elif d1[k] != d2[k] :
d1[k] = inconflict(d1[k], d2[k])
else :
d1[k] = d2[k]
return d1
默认情况下,它会解决冲突,转而支持第二个dict中的值,但你可以轻松地覆盖它,有些巫术你可能甚至可以抛出异常。 :)
答案 20 :(得分:0)
嘿,我也遇到了同样的问题,但是我有一个解决方案,我会把它发布在这里,以防它对其他人也有用,基本上是合并嵌套字典并添加值,对我来说,我需要计算一些概率,所以这个效果很好:
#used to copy a nested dict to a nested dict
def deepupdate(target, src):
for k, v in src.items():
if k in target:
for k2, v2 in src[k].items():
if k2 in target[k]:
target[k][k2]+=v2
else:
target[k][k2] = v2
else:
target[k] = copy.deepcopy(v)
使用上述方法,我们可以合并:
target = {'6,6':{'6,63':1},'63,4':{'4,4':1},'4,4':{'4,3' :1},'6,63':{'63,4':1}}
src = {'5,4':{'4,4':1},'5,5':{'5,4':1},'4,4':{'4,3' :1}}
,它将变为: {'5,5':{'5,4':1},'5,4':{'4,4':1},'6,6':{'6,63':1},' 63,4':{'4,4':1},'4,4':{'4,3':2},'6,63':{'63,4':1}}
还请注意此处的更改:
target = {'6,6':{'6,63':1},'6,63':{'63,4':1},'4,4':{'4 ,3':1} ,'63,4':{'4,4':1}}
src = {'5,4':{'4,4':1},'4,3':{'3,4':1},'4,4':{'4 ,9':1} ,'3,4':{'4,4':1},'5,5':{'5,4':1}}
merge = {'5,4':{'4,4':1},'4,3':{'3,4':1},'6,63':{'63,4' :1},'5,5':{'5,4':1},'6,6':{'6,63':1},'3,4':{'4,4':1 },'63,4':{'4,4':1},'4,4':{'4,3':1,'4,9':1} } < / p>
别忘了还要添加导入副本:
import copy
答案 21 :(得分:0)
万一有人想要另一种解决此问题的方法,这是我的解决方案。
Virtues :简短,声明性和实用的样式(递归,无突变)。
潜在的缺点:这可能不是您要查找的合并。有关语义,请查阅文档字符串。
def deep_merge(a, b):
"""
Merge two values, with `b` taking precedence over `a`.
Semantics:
- If either `a` or `b` is not a dictionary, `a` will be returned only if
`b` is `None`. Otherwise `b` will be returned.
- If both values are dictionaries, they are merged as follows:
* Each key that is found only in `a` or only in `b` will be included in
the output collection with its value intact.
* For any key in common between `a` and `b`, the corresponding values
will be merged with the same semantics.
"""
if not isinstance(a, dict) or not isinstance(b, dict):
return a if b is None else b
else:
# If we're here, both a and b must be dictionaries or subtypes thereof.
# Compute set of all keys in both dictionaries.
keys = set(a.keys()) | set(b.keys())
# Build output dictionary, merging recursively values with common keys,
# where `None` is used to mean the absence of a value.
return {
key: deep_merge(a.get(key), b.get(key))
for key in keys
}
答案 22 :(得分:0)
from collections import defaultdict
from itertools import chain
class DictHelper:
@staticmethod
def merge_dictionaries(*dictionaries, override=True):
merged_dict = defaultdict(set)
all_unique_keys = set(chain(*[list(dictionary.keys()) for dictionary in dictionaries])) # Build a set using all dict keys
for key in all_unique_keys:
keys_value_type = list(set(filter(lambda obj_type: obj_type != type(None), [type(dictionary.get(key, None)) for dictionary in dictionaries])))
# Establish the object type for each key, return None if key is not present in dict and remove None from final result
if len(keys_value_type) != 1:
raise Exception("Different objects type for same key: {keys_value_type}".format(keys_value_type=keys_value_type))
if keys_value_type[0] == list:
values = list(chain(*[dictionary.get(key, []) for dictionary in dictionaries])) # Extract the value for each key
merged_dict[key].update(values)
elif keys_value_type[0] == dict:
# Extract all dictionaries by key and enter in recursion
dicts_to_merge = list(filter(lambda obj: obj != None, [dictionary.get(key, None) for dictionary in dictionaries]))
merged_dict[key] = DictHelper.merge_dictionaries(*dicts_to_merge)
else:
# if override => get value from last dictionary else make a list of all values
values = list(filter(lambda obj: obj != None, [dictionary.get(key, None) for dictionary in dictionaries]))
merged_dict[key] = values[-1] if override else values
return dict(merged_dict)
if __name__ == '__main__':
d1 = {'aaaaaaaaa': ['to short', 'to long'], 'bbbbb': ['to short', 'to long'], "cccccc": ["the is a test"]}
d2 = {'aaaaaaaaa': ['field is not a bool'], 'bbbbb': ['field is not a bool']}
d3 = {'aaaaaaaaa': ['filed is not a string', "to short"], 'bbbbb': ['field is not an integer']}
print(DictHelper.merge_dictionaries(d1, d2, d3))
d4 = {"a": {"x": 1, "y": 2, "z": 3, "d": {"x1": 10}}}
d5 = {"a": {"x": 10, "y": 20, "d": {"x2": 20}}}
print(DictHelper.merge_dictionaries(d4, d5))
输出:
{'bbbbb': {'to long', 'field is not an integer', 'to short', 'field is not a bool'},
'aaaaaaaaa': {'to long', 'to short', 'filed is not a string', 'field is not a bool'},
'cccccc': {'the is a test'}}
{'a': {'y': 20, 'd': {'x1': 10, 'x2': 20}, 'z': 3, 'x': 10}}
答案 23 :(得分:0)
我能想到的最简单的方法是:
#!/usr/bin/python
from copy import deepcopy
def dict_merge(a, b):
if not isinstance(b, dict):
return b
result = deepcopy(a)
for k, v in b.iteritems():
if k in result and isinstance(result[k], dict):
result[k] = dict_merge(result[k], v)
else:
result[k] = deepcopy(v)
return result
a = {1:{"a":'A'}, 2:{"b":'B'}}
b = {2:{"c":'C'}, 3:{"d":'D'}}
print dict_merge(a,b)
输出:
{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}
答案 24 :(得分:0)
我一直在测试你的解决方案,并决定在我的项目中使用这个:
def mergedicts(dict1, dict2, conflict, no_conflict):
for k in set(dict1.keys()).union(dict2.keys()):
if k in dict1 and k in dict2:
yield (k, conflict(dict1[k], dict2[k]))
elif k in dict1:
yield (k, no_conflict(dict1[k]))
else:
yield (k, no_conflict(dict2[k]))
dict1 = {1:{"a":"A"}, 2:{"b":"B"}}
dict2 = {2:{"c":"C"}, 3:{"d":"D"}}
#this helper function allows for recursion and the use of reduce
def f2(x, y):
return dict(mergedicts(x, y, f2, lambda x: x))
print dict(mergedicts(dict1, dict2, f2, lambda x: x))
print dict(reduce(f2, [dict1, dict2]))
将函数作为参数传递是扩展jterrace解决方案的关键,与所有其他递归解决方案一样。
答案 25 :(得分:0)
以下函数将b合并为a。
def mergedicts(a, b):
for key in b:
if isinstance(a.get(key), dict) or isinstance(b.get(key), dict):
mergedicts(a[key], b[key])
else:
a[key] = b[key]
return a
答案 26 :(得分:0)
还有另一个细微变化:
这是一个基于纯python3集的深度更新功能。它通过一次遍历一个级别来更新嵌套字典,并调用自身来更新每个下一级别的字典值:
def deep_update(dict_original, dict_update):
if isinstance(dict_original, dict) and isinstance(dict_update, dict):
output=dict(dict_original)
keys_original=set(dict_original.keys())
keys_update=set(dict_update.keys())
similar_keys=keys_original.intersection(keys_update)
similar_dict={key:deep_update(dict_original[key], dict_update[key]) for key in similar_keys}
new_keys=keys_update.difference(keys_original)
new_dict={key:dict_update[key] for key in new_keys}
output.update(similar_dict)
output.update(new_dict)
return output
else:
return dict_update
一个简单的例子:
x={'a':{'b':{'c':1, 'd':1}}}
y={'a':{'b':{'d':2, 'e':2}}, 'f':2}
print(deep_update(x, y))
>>> {'a': {'b': {'c': 1, 'd': 2, 'e': 2}}, 'f': 2}
答案 27 :(得分:0)
另一个答案呢?!这也避免了突变/副作用:
variables = {}
while True:
choice = input('Enter a choice: ')
answer = int(input('Enter an number: '))
variables[choice] = answer
next_ = input('Continue? (y/n) ')
if next_.lower() == 'n':
break
print(variables)
def merge(dict1, dict2):
output = {}
# adds keys from `dict1` if they do not exist in `dict2` and vice-versa
intersection = {**dict2, **dict1}
for k_intersect, v_intersect in intersection.items():
if k_intersect not in dict1:
v_dict2 = dict2[k_intersect]
output[k_intersect] = v_dict2
elif k_intersect not in dict2:
output[k_intersect] = v_intersect
elif isinstance(v_intersect, dict):
v_dict2 = dict2[k_intersect]
output[k_intersect] = merge(v_intersect, v_dict2)
else:
output[k_intersect] = v_intersect
return output
答案 28 :(得分:0)
当然,代码将取决于您解决合并冲突的规则。这是一个可以采用任意数量的参数并将它们递归地合并到任意深度的版本,而不使用任何对象变异。它使用以下规则来解决合并冲突:
{"foo": {...}}
优先于{"foo": "bar"}
){"a": 1}
,{"a", 2}
和{"a": 3}
,结果将为{"a": 3}
)try:
from collections import Mapping
except ImportError:
Mapping = dict
def merge_dicts(*dicts):
"""
Return a new dictionary that is the result of merging the arguments together.
In case of conflicts, later arguments take precedence over earlier arguments.
"""
updated = {}
# grab all keys
keys = set()
for d in dicts:
keys = keys.union(set(d))
for key in keys:
values = [d[key] for d in dicts if key in d]
# which ones are mapping types? (aka dict)
maps = [value for value in values if isinstance(value, Mapping)]
if maps:
# if we have any mapping types, call recursively to merge them
updated[key] = merge_dicts(*maps)
else:
# otherwise, just grab the last value we have, since later arguments
# take precedence over earlier arguments
updated[key] = values[-1]
return updated
答案 29 :(得分:0)
我尚未对此进行广泛测试,因此我们鼓励您提供反馈。
from collections import defaultdict
dict1 = defaultdict(list)
dict2= defaultdict(list)
dict3= defaultdict(list)
dict1= dict(zip(Keys[ ],values[ ]))
dict2 = dict(zip(Keys[ ],values[ ]))
def mergeDict(dict1, dict2):
dict3 = {**dict1, **dict2}
for key, value in dict3.items():
if key in dict1 and key in dict2:
dict3[key] = [value , dict1[key]]
return dict3
dict3 = mergeDict(dict1, dict2)
#sort keys alphabetically.
dict3.keys()