如何合并具有相同键或不同键的多个字典?

Posted

技术标签:

【中文标题】如何合并具有相同键或不同键的多个字典?【英文标题】:How to merge multiple dicts with same key or different key? 【发布时间】:2011-08-22 05:16:51 【问题描述】:

我有多个这样的字典/键值对:

d1 = key1: x1, key2: y1  
d2 = key1: x2, key2: y2  

我希望结果是一个新的字典(如果可能,以最有效的方式):

d = key1: (x1, x2), key2: (y1, y2)  

实际上,我希望结果 d 是:

d = key1: (x1.x1attrib, x2.x2attrib), key2: (y1.y1attrib, y2.y2attrib)  

如果有人告诉我如何获得第一个结果,我可以弄清楚其余的。

【问题讨论】:

@Salil:我们可以假设每个键都存在于所有字典中吗? merging Python dictionaries的可能重复 嗨 Space_C0wb0y,是的,所有字典中都有键。 指定所有字典是否具有相同的键是绝对关键的。 【参考方案1】:

使用下面的方法,我们可以合并两个具有相同键的字典。

def update_dict(dict1: dict, dict2: dict) -> dict:
output_dict = 
for key in dict1.keys():
    output_dict.update(key: [])
    if type(dict1[key]) != str:
        for value in dict1[key]:
            output_dict[key].append(value)
    else:
        output_dict[key].append(dict1[key])
    if type(dict2[key]) != str:
        for value in dict2[key]:
            output_dict[key].append(value)
    else:
        output_dict[key].append(dict2[key])

return output_dict

输入:d1 = key1: x1, key2: y1 d2 = key1: x2, key2: y2 输出:'key1': ['x1', 'x2'], 'key2': ['y1', 'y2']

【讨论】:

【参考方案2】:

pandas Data FrameIMO 更好地表示两个或多个具有相同键的字典:

d1 = "key1": "x1", "key2": "y1"  
d2 = "key1": "x2", "key2": "y2"  
d3 = "key1": "x3", "key2": "y3"  

d1_df = pd.DataFrame.from_dict(d1, orient='index')
d2_df = pd.DataFrame.from_dict(d2, orient='index')
d3_df = pd.DataFrame.from_dict(d3, orient='index')

fin_df = pd.concat([d1_df, d2_df, d3_df], axis=1).T.reset_index(drop=True)
fin_df

    key1 key2
0   x1   y1
1   x2   y2
2   x3   y3

【讨论】:

【参考方案3】:

即使两个字典中的键不同,这个函数也会合并两个字典:

def combine_dict(d1, d2):
    return 
        k: tuple(d[k] for d in (d1, d2) if k in d)
        for k in set(d1.keys()) | set(d2.keys())
    

例子:

d1 = 
    'a': 1,
    'b': 2,

d2` = 
    'b': 'boat',
    'c': 'car',

combine_dict(d1, d2)
# Returns: 
#    'a': (1,),
#    'b': (2, 'boat'),
#    'c': ('car',)
# 

【讨论】:

【参考方案4】:

假设有两个字典具有完全相同的键,下面是最简洁的方法(python3 应该用于两种解决方案)。


d1 = 'a': 1, 'b': 2, 'c':3
d2 = 'a': 5, 'b': 6, 'c':7 

# get keys from one of the dictionary
ks = [k for k in d1.keys()]

print(ks)
['a', 'b', 'c']

# call values from each dictionary on available keys
d_merged = k: (d1[k], d2[k]) for k in ks

print(d_merged)
'a': (1, 5), 'b': (2, 6), 'c': (3, 7)

# to merge values as list
d_merged = k: [d1[k], d2[k]] for k in ks
print(d_merged)
'a': [1, 5], 'b': [2, 6], 'c': [3, 7]

如果有两个字典有一些共同的键,但有几个不同的键,则应准备所有键的列表。


d1 = 'a': 1, 'b': 2, 'c':3, 'd': 9
d2 = 'a': 5, 'b': 6, 'c':7, 'e': 4 

# get keys from one of the dictionary
d1_ks = [k for k in d1.keys()]
d2_ks = [k for k in d2.keys()]

all_ks = set(d1_ks + d2_ks)

print(all_ks)
['a', 'b', 'c', 'd', 'e']

# call values from each dictionary on available keys
d_merged = k: [d1.get(k), d2.get(k)] for k in all_ks

print(d_merged)
'd': [9, None], 'a': [1, 5], 'b': [2, 6], 'c': [3, 7], 'e': [None, 4]

【讨论】:

【参考方案5】:

如果键是嵌套的:

d1 =  'key1':  'nkey1': 'x1' , 'key2':  'nkey2': 'y1'   
d2 =  'key1':  'nkey1': 'x2' , 'key2':  'nkey2': 'y2'  
ds = [d1, d2]
d = 
for k in d1.keys():
    for k2 in d1[k].keys():
        d.setdefault(k, )
        d[k].setdefault(k2, [])
        d[k][k2] = tuple(d[k][k2] for d in ds)

产量:

'key1': 'nkey1': ('x1', 'x2'), 'key2': 'nkey2': ('y1', 'y2')

【讨论】:

【参考方案6】:

假设您拥有所有键的列表(您可以通过遍历所有字典并获取它们的键来获取此列表)。让我们将其命名为listKeys。另外:

listValues 是您想要的单个键的所有值的列表 合并。 allDicts: 你要合并的所有字典。
result = 
for k in listKeys:
    listValues = [] #we will convert it to tuple later, if you want.
    for d in allDicts:
       try:
            fileList.append(d[k]) #try to append more values to a single key
        except:
            pass
    if listValues: #if it is not empty
        result[k] = typle(listValues) #convert to tuple, add to new dictionary with key k

【讨论】:

【参考方案7】:

这个库帮助了我,我有一个嵌套键的字典列表,它们具有相同的名称但具有不同的值,所有其他解决方案都不断覆盖这些嵌套键。

https://pypi.org/project/deepmerge/

from deepmerge import always_merger

def process_parms(args):
    temp_list = []
    for x in args:
        with open(x, 'r') as stream:
            temp_list.append(yaml.safe_load(stream))

    return always_merger.merge(*temp_list)

【讨论】:

【参考方案8】:

这是一个通用的解决方案,可以处理任意数量的字典,当键只在一些字典中时:

from collections import defaultdict

d1 = 1: 2, 3: 4
d2 = 1: 6, 3: 7

dd = defaultdict(list)

for d in (d1, d2): # you can list as many input dicts as you want here
    for key, value in d.items():
        dd[key].append(value)

print(dd)

演出:

defaultdict(<type 'list'>, 1: [2, 6], 3: [4, 7])

另外,要获取您的.attrib,只需将append(value) 更改为append(value.attrib)

【讨论】:

我认为 OP 想要的值是 tuple 而不是 list @A A:真的很重要吗?在多个输入字典的更一般情况下构建元组将更加棘手,其中一些键不存在于任何地方,恕我直言 然后您可能希望从defaultdict 中创建一个正常的dict,以便您对不存在的键等具有正常的dict 行为:dd = dict(dd) @Ned:好点,但这取决于数据的最终用途 @Eli:不,这没关系,但我只是试图根据 OP 的要求,并希望你能找到元组的解决方案 :-)【参考方案9】:

来自 blub 的回答:

您也可以使用每个列表中的值直接形成元组

ds = [d1, d2]
d = 
for k in d1.keys():
  d[k] = (d1[k], d2[k])

如果您对元组有特定的顺序,这可能会很有用

ds = [d1, d2, d3, d4]
d = 
for k in d1.keys():
  d[k] = (d3[k], d1[k], d4[k], d2[k]) #if you wanted tuple in order of d3, d1, d4, d2

【讨论】:

【参考方案10】:

为了补充两个列表的解决方案,这里有一个处理单个列表的解决方案。

一个示例列表(NetworkX 相关;为便于阅读在此处手动格式化):

ec_num_list = [((src, tgt), ec_num['ec_num']) for src, tgt, ec_num in G.edges(data=True)]

print('\nec_num_list:\n'.format(ec_num_list))
ec_num_list:
[((82, 433), '1.1.1.1'),
  ((82, 433), '1.1.1.2'),
  ((22, 182), '1.1.1.27'),
  ((22, 3785), '1.2.4.1'),
  ((22, 36), '6.4.1.1'),
  ((145, 36), '1.1.1.37'),
  ((36, 154), '2.3.3.1'),
  ((36, 154), '2.3.3.8'),
  ((36, 72), '4.1.1.32'),
  ...] 

注意相同边的重复值(由元组定义)。将这些“值”与它们对应的“键”进行比较:

from collections import defaultdict
ec_num_collection = defaultdict(list)
for k, v in ec_num_list:
    ec_num_collection[k].append(v)

print('\nec_num_collection:\n'.format(ec_num_collection.items()))
ec_num_collection:
[((82, 433), ['1.1.1.1', '1.1.1.2']),   ## << grouped "values"
((22, 182), ['1.1.1.27']),
((22, 3785), ['1.2.4.1']),
((22, 36), ['6.4.1.1']),
((145, 36), ['1.1.1.37']),
((36, 154), ['2.3.3.1', '2.3.3.8']),    ## << grouped "values"
((36, 72), ['4.1.1.32']),
...] 

如果需要,将该列表转换为字典:

ec_num_collection_dict = k:v for k, v in zip(ec_num_collection, ec_num_collection)

print('\nec_num_collection_dict:\n'.format(dict(ec_num_collection)))
  ec_num_collection_dict:
  (82, 433): ['1.1.1.1', '1.1.1.2'],
  (22, 182): ['1.1.1.27'],
  (22, 3785): ['1.2.4.1'],
  (22, 36): ['6.4.1.1'],
  (145, 36): ['1.1.1.37'],
  (36, 154): ['2.3.3.1', '2.3.3.8'],
  (36, 72): ['4.1.1.32'],
  ...

参考文献

[此线程]How to merge multiple dicts with same key? [Python 文档]https://docs.python.org/3.7/library/collections.html#collections.defaultdict

【讨论】:

【参考方案11】:

假设所有键始终存在于所有字典中:

ds = [d1, d2]
d = 
for k in d1.iterkeys():
    d[k] = tuple(d[k] for d in ds)

注意:在 Python 3.x 中使用以下代码:

ds = [d1, d2]
d = 
for k in d1.keys():
  d[k] = tuple(d[k] for d in ds)

如果 dic 包含 numpy 数组:

ds = [d1, d2]
d = 
for k in d1.keys():
  d[k] = np.concatenate(list(d[k] for d in ds))

【讨论】:

我认为只要“for k in d1”就可以了。 和 d.get(k, None) 代替 d[k] @tahir 这意味着字典具有不匹配的键,因此迭代 d1 是不正确的(它可能会丢失其他字典中的键)。 对于 python 3 用户:d1.iterkeys() =d1.items() 它在 Python3.x 中仍然对我不起作用。即使我的值不是数组,我也试过这个,它可以工作。但是,输出的值将是数组。 ***.com/questions/54040858/…【参考方案12】:
dict1 = 'm': 2, 'n': 4
dict2 = 'n': 3, 'm': 1

确保键的顺序相同:

dict2_sorted = i:dict2[i] for i in dict1.keys()

keys = dict1.keys()
values = zip(dict1.values(), dict2_sorted.values())
dictionary = dict(zip(keys, values))

给出:

'm': (2, 1), 'n': (4, 3)

【讨论】:

values() 中的元素顺序未定义,因此您可能正在合并来自不相关键的值。 我刚刚应用了更改,因此它现在可以捕获您的反馈 我认为更改不会解决问题。您需要使用sorted(d.items())sorted(d.keys()) 来获得可预测的结果。 你能举个例子来证明这一点吗? dict2_sorted 是python中的排序字典! 我对此做了一个小的研究。在最新版本的 Python(3.6+)中,迭代顺序开始与插入顺序相匹配(参见例如here),这使您的代码通过。但这被认为是不应依赖的实现细节。我的第二个示例(请参阅here)在使用旧 Python 3.4 的onlinegdb 中确实失败了。其他在线解释器使用较新的 Python,因此无法在此处重现问题。【参考方案13】:

Python 3.x 更新

来自 Eli Bendersky 的回答:

Python 3 删除了 dict.iteritems 改为使用 dict.items。 参见 Python 维基:https://wiki.python.org/moin/Python3.0

from collections import defaultdict

dd = defaultdict(list)

for d in (d1, d2):
    for key, value in d.items():
        dd[key].append(value)

【讨论】:

【参考方案14】:

一个紧凑的可能性

d1='a':1,'b':2
d2='c':3,'d':4
context=**d1, **d2
context
'b': 2, 'c': 3, 'd': 4, 'a': 1

【讨论】:

问题是关于合并具有相同键的字典。你不是必需的答案。【参考方案15】:
def merge(d1, d2, merge):
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge(result[k], v)
        else:
            result[k] = v
    return result

d1 = 'a': 1, 'b': 2
d2 = 'a': 1, 'b': 3, 'c': 2
print merge(d1, d2, lambda x, y:(x,y))

'a': (1, 1), 'c': 2, 'b': (2, 3)

【讨论】:

【参考方案16】:

这是您可以使用的一种方法,即使两个字典没有相同的键也可以使用:

d1 = 'a':'test','b':'btest','d':'dreg'
d2 = 'a':'cool','b':'main','c':'clear'

d = 

for key in set(d1.keys() + d2.keys()):
    try:
        d.setdefault(key,[]).append(d1[key])        
    except KeyError:
        pass

    try:
        d.setdefault(key,[]).append(d2[key])          
    except KeyError:
        pass

print d

这将生成以下输入:

'a': ['test', 'cool'], 'c': ['clear'], 'b': ['btest', 'main'], 'd': ['dreg']

【讨论】:

可以将答案中的set(d1.keys() + d2.keys()) 更改为set(list(d1.keys()) + list(d2.keys()))(对于Python 3.x)吗?否则会在 python3.x 中抛出 TypeError: unsupported operand type(s) for +: 'dict_keys' and 'dict_keys' 错误【参考方案17】:

如果你只有 d1 和 d2,

from collections import defaultdict

d = defaultdict(list)
for a, b in d1.items() + d2.items():
    d[a].append(b)

【讨论】:

以上是关于如何合并具有相同键或不同键的多个字典?的主要内容,如果未能解决你的问题,请参考以下文章

具有相同键的多个条目的字典[重复]

Python字典相同值的键合并

具有相同键的(嵌套)字典的 Pythonic 替代方案?

Microsoft Access - 合并具有相同字段的多个表

如何将字典的输出配对以打印具有相同键的值? [关闭]

如何将在多行中具有相同键的字典转换为数据框[重复]