【问题标题】:Merge two dictionaries and keep the values for duplicate keys in Python合并两个字典并在 Python 中保留重复键的值
【发布时间】:2023-03-28 19:29:01
【问题描述】:

假设我有两个字典:

dic1 =  { "first":1, "second":4, "third":8} 
dic2 =  { "first":9, "second":5, "fourth":3}

有没有一种简单的方法来获得类似下面的东西?

dic3 =  { "first":[1,9], "second":[4,5], "third":[8], "fourth":[3]}

我使用列表来存储值,但元组也可以。

【问题讨论】:

  • 您确定要只出现在一本字典中的项目是裸项目,而不是一个项目的列表吗?
  • @interfect 好的,我明白你的意思了,我正在编辑

标签: python dictionary merge


【解决方案1】:

这是一个幼稚的解决方案;将其中一个字典复制到结果并迭代另一个字典的键和值,根据需要将列表添加到结果中。由于只有两个字典,因此合并列表不会超过 2 个项目。

dic1 = {"first": 1, "second": 4, "third": 8} 
dic2 = {"first": 9, "second": 5, "fourth": 3}
dic3 = dict(dic2)

for k, v in dic1.items():
    dic3[k] = [dic3[k], v] if k in dic3 else v

print(dic3) # => {'first': [9, 1], 'second': [5, 4], 'fourth': 3, 'third': 8}

如果您希望单个值成为列表(可能是更好的设计;混合类型不是很有趣),您可以使用:

dic3 = {k: [v] for k, v in dic2.items()}

for k, v in dic1.items():
    dic3[k] = dic3[k] + [v] if k in dic3 else [v]

print(dic3) # => {'first': [9, 1], 'second': [5, 4], 'fourth': [3], 'third': [8]}

将其推广到任意数量的字典:

def merge_dicts(*dicts):
    """
    >>> merge_dicts({"a": 2}, {"b": 4, "a": 3}, {"a": 1})
    {'a': [2, 3, 1], 'b': [4]}
    """
    merged = {}
    
    for d in dicts:
        for k, v in d.items():
            if k not in merged:
                merged[k] = []

            merged[k].append(v)
    
    return merged

如果您不介意导入,可以使用collections.defaultdict 清理一下:

from collections import defaultdict

def merge_dicts(*dicts):
    """
    >>> merge_dicts({"a": 2}, {"b": 4, "a": 3}, {"a": 1})
    defaultdict(<class 'list'>, {'a': [2, 3, 1], 'b': [4]})
    """
    merged = defaultdict(list)
    
    for d in dicts:
        for k, v in d.items():
            merged[k].append(v)
    
    return merged

【讨论】:

  • 如果 OP 想要单元素列表,可以使用 [*set([dic3[k], v])] 代替三元 if 表达式。
【解决方案2】:

给定:

dic1 =  { "first":1, "second":4, "third":8} 
dic2 =  { "first":9, "second":5, "fourth":3}

你可以使用.setdefault:

dic_new={}
for k,v in list(dic1.items())+list(dic2.items()):
    dic_new.setdefault(k, []).append(v)
else:
    dic_new={k:v if len(v)>1 else v[0] for k,v in dic_new.items()}  

>>> dic_new
{'first': [1, 9], 'second': [4, 5], 'third': 8, 'fourth': 3}

这会产生有问题的输出。我认为将单个元素列表展平为不同的对象类型是不必要的复杂性。


通过编辑,这将产生所需的结果:

dic_new={}
for k,v in list(dic1.items())+list(dic2.items()):
    dic_new.setdefault(k, []).append(v)

>>> dic_new
{'first': [1, 9], 'second': [4, 5], 'third': [8], 'fourth': [3]}

【讨论】:

    【解决方案3】:

    您可以使用defaultdict 来保存列表,然后将值附加到它们。这种方法很容易扩展到任意数量的字典。

    from collections import defaultdict
    
    dd = defaultdict(list)
    
    dics = [dic1, dic2]
    for dic in dics:
        for key, val in dic.iteritems():  # .items() in Python 3.
            dd[key].append(val)
    
    >>> dict(dd)
    {'first': [1, 9], 'fourth': [3], 'second': [4, 5], 'third': [8]}
    

    所有具有单个值的键仍然保存在一个列表中,这可能是最好的方法。但是,您可以将任何长度为 1 的值更改为实际值,例如

    for key, val in dd.iteritems():  # .items() in Python 3.
        if len(val) == 1
            dd[key] = val[0]
    

    【讨论】:

    • 即使键中没有重复值,此实现也会使生成的defaultdict 中的所有值都为list 类型。如果首选行为是将单个值保留为该值的类型,那么我们应该在添加之前检查键是否存在,并且仅当该键已经存在时才转换为列表。
    • @ctj232 OP 更正了问题,将它们放在列表结构中,无论如何这更有意义。
    【解决方案4】:

    使用集合和字典理解

    L = [d1, d2]
    dups = set(d1.keys() & d2.keys())
    d = {k: [L[0][k], L[1][k]] if k in dups else i[k] for i in L for k in i}
    
    {'first': [1, 9], 'second': [4, 5], 'third': 8, 'fourth': 3}
    

    【讨论】:

      【解决方案5】:

      一般来说,我会说将不同键的值转换为不同的对象类型是不好的做法。我会简单地做这样的事情:

      def merge_values(val1, val2):
          if val1 is None:
              return [val2]
          elif val2 is None:
              return [val1]
          else:
              return [val1, val2]
      dict3 = {
          key: merge_values(dic1.get(key), dic2.get(key))
          for key in set(dic1).union(dic2)
      }
      

      【讨论】:

      • 好的,我已经进行了编辑以在任何地方都使用相同的类型。
      【解决方案6】:

      创建一个新字典 dic,其中键为 dic1dic2 的键并为空列表赋值,然后遍历 dic1dic2 将值附加到 dic

      dic1 =  { "first":1, "second":4, "third":8} 
      dic2 =  { "first":9, "second":5, "fourth":3}
      
      dic = {key:[] for key in list(dic1.keys()) + list(dic2.keys())}
      
      for key in dic1.keys():
          dic[key].append(dic1[key])
      
      for key in dic2.keys():
          dic[key].append(dic2[key])
      

      【讨论】:

      • Python 2 仅在编写时使用。 Python 3 将在此处提供 TypeErrordic1.keys() + dic2.keys()
      • 将 dict.keys() 转换为 list 可以兼容 python3。我从未搜索过为什么像sum 这样的基本函数不再适用于python3 中的任意类型。 sum 在字符串列表上非常方便。
      • 现在可以使用dic = {key:[] for key in {k for k in list(dic1) + list(dic2)}} 使其更高效,从而消除重复键。
      • 集转换仍会读取整个列表以创建集。我没有转换它,因为它会进一步加重符号的负担,并且不会在内存方面获得任何收益。
      • Python 只是想保护你。使用sum 连接字符串列表将是Shlemiel the painter 算法的经典示例。连接 n 个字符串将创建 n-2 个临时字符串。
      【解决方案7】:
      from copy import deepcopy
      
      
      def _add_value_to_list(value, lis):
          if value:
              if isinstance(value, list):
                  lis.extend(value)
              else:
                  lis.append(value)
          else:
              pass
      
      
      def _merge_value(value_a, value_b):
          merged_value = []
          _add_value_to_list(value_a, merged_value)
          _add_value_to_list(value_b, merged_value)
          return merged_value
      
      
      def _recursion_merge_dict(new_dic, dic_a, dic_b):
          if not dic_a or not dic_b:
              return new_dic
          else:
              if isinstance(new_dic, dict):
                  for k, v in new_dic.items():
                      new_dic[k] = _recursion_merge_dict(v, dic_a.get(k, {}), dic_b.get(k, {}))
                  return new_dic
              else:
                  return _merge_value(dic_a, dic_b)
      
      
      def merge_dicts(dic_a, dic_b):
          new_dic = deepcopy(dic_a)
          new_dic.update(dic_b)
      
          return _recursion_merge_dict(new_dic, dic_a, dic_b)
      

      【讨论】:

        【解决方案8】:

        列表字典的解决方案(改编自@dawg):

        dic1 =  { "first":[1], "second":[4], "third":[8]} 
        dic2 =  { "first":[9], "second":[5], "fourth":[3]}
        dic_new={}
        for k,v in list(dic1.items())+list(dic2.items()):
            dic_new.setdefault(k, []).extend(v)
        >>> dic_new
        {'first': [1, 9], 'second': [4, 5], 'third': [8], 'fourth': [3]}
        

        【讨论】:

          猜你喜欢
          • 2014-09-04
          • 1970-01-01
          • 2023-03-30
          • 2017-10-04
          • 1970-01-01
          • 2023-02-03
          • 2020-08-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多