【问题标题】:Dictionary merge by updating but not overwriting if value exists字典通过更新合并,但如果值存在则不覆盖
【发布时间】:2011-09-15 07:33:51
【问题描述】:

如果我有 2 个字典如下:

d1 = {'a': 2, 'b': 4}
d2 = {'a': 2, 'b': ''}

为了“合并”它们:

dict(d1.items() + d2.items())

结果

{'a': 2, 'b': ''}

但是,如果我想比较两个字典的每个值,并且仅在 d1 中的值为空/None/'' 时将d2 更新为d1,我该怎么办?

当存在相同的键时,我只想保留数值(来自d1d2)而不是空值。如果两个值都为空,则保持空值没有问题。如果两者都有值,那么d1-value 应该保留。

d1 = {'a': 2, 'b': 8, 'c': ''}
d2 = {'a': 2, 'b': '', 'c': ''}

应该会导致

{'a': 2, 'b': 8, 'c': ''}

其中 8 未被'' 覆盖。

【问题讨论】:

标签: python dictionary merge compare


【解决方案1】:

使用d1 键/值对更新d2,但前提是d1 值不是None''(假):

>>> d1 = dict(a=1, b=None, c=2)
>>> d2 = dict(a=None, b=2, c=1)
>>> d2.update({k: v for k, v in d1.items() if v})
>>> d2
{'a': 1, 'c': 2, 'b': 2}

(在 Python 2 中使用 iteritems() 而不是 items()。)

【讨论】:

  • ... 这将更改输入 d2。为什么不dr={}; dr.update(d1); dr.update((k,v) for (k,v) in d2.items() if v)
  • 这对我有用d2.update({k:v for k,v in d1.iteritems() if v is not None})
【解决方案2】:

如果你想忽略空格,例如合并:

a = {"a": 1, "b": 2, "c": ""}
b = {"a": "", "b": 4, "c": 5}
c = {"a": "aaa", "b": ""}
d = {"a": "", "w": ""}

结果:{'a': 'aaa', 'b': 4, 'c': 5, 'w': ''}

您可以使用这两个功能:

def merge_two_dicts(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_two_dicts(a[key], b[key], path + [str(key)])
            elif a[key] == b[key]:
                pass  # same leaf value
            else:
                if a[key] and not b[key]:
                    a[key] = a[key]
                else:
                    a[key] = b[key]
        else:
            a[key] = b[key]
    return a


def merge_multiple_dicts(*a):
    output = a[0]
    if len(a) >= 2:
        for n in range(len(a) - 1):
            output = merge_two_dicts(output, a[n + 1])

    return output

所以你可以使用merge_multiple_dicts(a,b,c,d)

【讨论】:

    【解决方案3】:

    仅合并非零值

    为此,我们可以创建一个没有空值的字典,然后将它们合并在一起:

    d1 = {'a':1, 'b':1, 'c': '', 'd': ''}
    d2 = {'a':2, 'c':2, 'd': ''}
    merged_non_zero = {
        k: (d1.get(k) or d2.get(k))
        for k in set(d1) | set(d2)
    }
    print(merged_non_zero)
    

    输出:

    {'a': 1, 'b': 1, 'c': 2, 'd': ''}
    
    • a -> 更喜欢 d1 中的第一个值,因为 d1 和 d2 上都存在“a”
    • b -> 只存在于 d1
    • c -> d2 上非零
    • d -> 两个都是空字符串

    说明

    上面的代码将使用字典理解创建一个字典。

    如果d1 具有值及其非零值(即bool(val) is True),它将使用d1[k] 值,否则将使用d2[k]

    请注意,我们还合并了两个字典的所有键,因为它们可能使用 set union - set(d1) | set(d2) 具有不完全相同的键。

    Python 3.5+ 文字字典

    除非使用过时的python版本,否则最好使用它。

    字典解包的 Pythonic 和更快的方法:

    d1 = {'a':1, 'b':1}
    d2 = {'a':2, 'c':2}
    merged = {**d1, **d2}  # priority from right to left
    print(merged)
    
    {'a': 2, 'b': 1, 'c': 2}
    

    它比 dict(list(d2.items()) + list(d1.items())) 替代方案更简单也更快:

    d1 = {i: 1 for i in range(1000000)}
    d2 = {i: 2 for i in range(2000000)}
    
    %timeit dict(list(d1.items()) + list(d2.items())) 
    402 ms ± 33.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit {**d1, **d2}
    144 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    更多来自PEP448:

    字典中的键保持从右到左的优先顺序,因此 {**{'a': 1}, 'a': 2, **{'a': 3}} 计算结果为 { “一”:3}。拆包的次数和位置没有限制。

    【讨论】:

    • 这个答案是完全错误的,除非你用{**d2, **d1} 来做。如果您不希望 d2 覆盖 d1 中的值,您仍然需要反转字典。
    • 感谢@drhagen,我已经更新了它以包含更好的答案以及我对如何合并的建议
    【解决方案4】:

    只需切换顺序:

    z = dict(d2.items() + d1.items())
    

    顺便说一下,您可能还对可能更快的update 方法感兴趣。

    在 Python 3 中,您必须先将视图对象转换为列表:

    z = dict(list(d2.items()) + list(d1.items())) 
    

    如果要对空字符串进行特殊处理,可以执行以下操作:

    def mergeDictsOverwriteEmpty(d1, d2):
        res = d2.copy()
        for k,v in d2.items():
            if k not in d1 or d1[k] == '':
                res[k] = v
        return res
    

    【讨论】:

    • 我认为,在这种情况下.. 如果d1 有空项目值,它会覆盖d2 项目值,它有数值?
    • @siva 更新了您的特殊情况。
    • 我想应该是res=d1.copy(),否则dicts之间没有信息传递。
    • Python 3.4.3 至少不支持字典项集之间的+但是您可以通过转换为list 来获得相同的结果:@987654331 @
    • itertools.chain() 也可能有帮助。
    【解决方案5】:

    要从d1 添加到d1 中不存在的d2 中的键/值,而不覆盖d2 中的任何现有键/值:

    temp = d2.copy()
    d2.update(d1)
    d2.update(temp)
    

    【讨论】:

      【解决方案6】:

      这是一个就地解决方案(它修改了 d2):

      # assumptions: d2 is a temporary dict that can be discarded
      # d1 is a dict that must be modified in place
      # the modification is adding keys from d2 into d1 that do not exist in d1.
      
      def update_non_existing_inplace(original_dict, to_add):
          to_add.update(original_dict) # to_add now holds the "final result" (O(n))
          original_dict.clear() # erase original_dict in-place (O(1))
          original_dict.update(to_add) # original_dict now holds the "final result" (O(n))
          return
      

      这是另一个就地解决方案,它不太优雅但可能更高效,并且不修改 d2

      # assumptions: d2 is can not be modified
      # d1 is a dict that must be modified in place
      # the modification is adding keys from d2 into d1 that do not exist in d1.
      
      def update_non_existing_inplace(original_dict, to_add):
          for key in to_add.iterkeys():
              if key not in original_dict:
                  original_dict[key] = to_add[key]
      

      【讨论】:

        【解决方案7】:

        如果您有相同大小和键的字典,您可以使用以下代码:

        dict((k,v if k in d2 and d2[k] in [None, ''] else d2[k]) for k,v in d1.iteritems())
        

        【讨论】:

        • 不幸的是,我的字典不是一些大小和键,只是一些具有差异值的相同键的出现。
        • @siva:如果您遇到这种情况,我已经修改了代码以检查 d1 中的 d2
        【解决方案8】:

        d2.update(d1) 而不是dict(d2.items() + d1.items())

        【讨论】:

        • ... 会改变 d2 的内容,这可能不是 OP 想要的。至少,dict(d1.items()+d2.items()) 保持输入不变。
        猜你喜欢
        • 1970-01-01
        • 2020-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-18
        • 2020-02-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多