【问题标题】:python merge dict iterately and elegantly?python 迭代地和优雅地合并字典?
【发布时间】:2017-05-17 07:59:19
【问题描述】:

我有 2 个字典,例如:

x = {2: {'a':1, 'b':1}}
y = {3: {'a':1}, 2: {'a':2, 'c':2}}

我想要的合并结果应该是:

z = {3: {'a':1}, 2: {'a':3, 'b':1, 'c':2}}

我试过dict(x.items() + y.items())Counter,但只得到

{2: {'a': 2, 'c': 2}, 3: {'a': 1}}

结果。

如何合并其值也是 dict 本身的 dict?


我已经解释了How to merge two Python dictionaries in a single expression?之间的区别 是不是我的字典的值也是字典。

【问题讨论】:

标签: python dictionary


【解决方案1】:

这个呢:

对于 Python2.x:

from collections import Counter

y = {2: {'a': 1, 'b': 1}, 1: {'a': 1, 'b': 1}}
x = {3: {'a': 1}, 2: {'a': 2, 'c': 2}}

t=x.copy()
print(dict({k: (dict(Counter(t.pop(k, None)) + Counter(v))) for k, v in y.items()},**t))

结果:

{1: {'a': 1, 'b': 1}, 2: {'a': 3, 'c': 2, 'b': 1}, 3: {'a': 1}}

对于 Python3.5+:

{**{k: (dict(Counter(t.pop(k, None)) + Counter(v))) for k, v in y.items()},**t}

【讨论】:

  • 谢谢。但如果 y = {},答案将是 {},期望的应该是 {2: {'a':1, 'b':1}}
  • 试试这个测试用例:x = {221: {'bm': 0.0, 'tp': -144.0, 'ac': 9, 'da': 9, 'bc': 1, 'wc': 1, 'bd': 8}} y = {10: {'tp': 0, 'ac': 3, 'da': 294.0, 'bc': 1, 'wc': 1, ' bd': 2, 'mk': 0.0}},你会发现缺少一些键。
【解决方案2】:

这里有一个可能的解决方案。虽然它不使用任何库,但它相当复杂。

它还可以与任何两个字典一起使用,独立于哪个字典最大。

{key: {ikey: x.get(key, {}).get(ikey, 0) + y.get(key, {}).get(ikey, 0)  
    for ikey in x.get(key, {}).keys()+y.get(key, {}).keys()} 
 for key in x.keys()+y.keys()}

输出:

{2: {'a': 3, 'b': 1, 'c': 2}, 3: {'a': 1}}

【讨论】:

    【解决方案3】:

    假设对于某个键的任何相同深度,在两个字典中,值的类型相同(两个字典或两个数字,我不知道如何定义将数字合并到字典中)。

    def merge(d1, d2):
        for i in d2:
            if i not in d1:
                continue
           if isinstance(d1[i], dict) and isinstance(d2[i], dict):
                merge(d1[i], d2[i])
            else:
                d2[i] += d1[i]
            d1.pop(i)
        for j in d1:
            d2[j] = d1[j]
    

    这很简单,但可能并不优雅。

    【讨论】:

      【解决方案4】:

      使用collections.Counter“合并”您要合并的项目(仅公共键),并保持其他项目不变,在字典理解中使用双三元表达式,迭代键的联合:

      import collections
      x = {2: {'a':1, 'b':1}}
      y = {3: {'a':1}, 2: {'a':2, 'c':2}}
      
      # pre-compute common keys, one-liners are not the ultimate goal in life!
      common_keys = set(x) & set(y)
      
      # now generate the merged dict
      result = {k:collections.Counter(x[k])+collections.Counter(y[k]) if k in common_keys else x[k] if k in x else y[k] for k in set(x)|set(y)}
      print(result)
      

      结果:

      {2: Counter({'a': 3, 'c': 2, 'b': 1}), 3: {'a': 1}}
      

      注意:我们可以通过替换来避免预先计算公共键:

      if k in common_keys
      

      通过

      if k in x and k in y
      

      【讨论】:

        【解决方案5】:

        这行得通:

        x = {2: {'a':1, 'b':1}}
        y = {3: {'a':1}, 2: {'a':2, 'c':2}}
        
        
        def key_merge (dict1, dict2):   
        ## function which checks for any matching keys, and merges them   
        if len(dict1.keys()) == 0 or len(dict2.keys()) == 0:
            return {}
        else:    
            for key in dict1.keys():
                if key in dict2.keys():
                  return {key:{ k: dict1[key].get(k, 0) + dict2[key].get(k, 0) for k in set(dict1[key])|set(dict2[key]) }}
        
        z = {**x, **y, **key_merge(x, y)}
        

        再次,是否优雅由您决定!

        【讨论】:

          【解决方案6】:

          我不知道你对“优雅”的定义是什么,但假设你的意思是可读,那么也许这种方式适合你。

          from collections import Counter
          
          
          def counter_dict(in_dict):
              """
              Create a dict of Counters from a dict of dicts.
              """
              return dict((k, Counter(v)) for (k, v) in in_dict.items())
          
          
          def merge_counter_dicts(a, b):
              """
              Create a dict of Counters from two dicts of Counters.
              Where keys exist in both counts are summed.
              """
              out_dict = a.copy()
              for k in b.keys():
                  out_dict[k] = out_dict.setdefault(k, Counter()) + b[k]
              return out_dict
          
          x = {2: {'a': 1, 'b': 1}}
          y = {3: {'a': 1}, 2: {'a': 2, 'c': 2}}
          
          xc = counter_dict(x)
          xy = counter_dict(y)
          
          print merge_counter_dicts(xc, xy)
          

          【讨论】:

            【解决方案7】:
            # What about something like this:
            
            # merge_nested_dicts.py
            
            # tested under Python3.6
            # assuming dict_02 overwrites dict_01
            # one liner functional style
            def deep_merge(dict_01, dict_02):
                return {k: {**dict_01.get(k), **dict_02.get(k)} if k in dict_01 and
                            isinstance(dict_01.get(k), dict) and
                            isinstance(dict_02.get(k), dict) else v 
                            for k, v in {**dict_01, **dict_02}.items()}              
            
            if __name__ == '__main__':
                y = {2: {'a': 1, 'b': 1}, 1: {'a': 1, 'b': 1}}
                x = {3: {'a': 1}, 2: {'a': 2, 'c': 2}}
            
                print(x)
                print(y)
                print(deep_merge(x, y))
            
            '''
            {3: {'a': 1}, 2: {'a': 2, 'c': 2}}
            {2: {'a': 1, 'b': 1}, 1: {'a': 1, 'b': 1}}
            {3: {'a': 1}, 2: {'a': 1, 'c': 2, 'b': 1}, 1: {'a': 1, 'b': 1}}
            '''
            

            【讨论】:

              猜你喜欢
              • 2012-07-02
              • 2011-05-23
              • 2015-07-07
              • 1970-01-01
              • 1970-01-01
              • 2014-05-29
              • 1970-01-01
              • 1970-01-01
              • 2010-11-24
              相关资源
              最近更新 更多