Is there any pythonic way to combine two dicts (adding values for keys that appear in both)?

前端 未结 17 2363
梦毁少年i
梦毁少年i 2020-11-22 01:50

For example I have two dicts:

Dict A: {\'a\': 1, \'b\': 2, \'c\': 3}
Dict B: {\'b\': 3, \'c\': 4, \'d\': 5}

I need a pythonic way of \'comb

相关标签:
17条回答
  • 2020-11-22 02:32
    import itertools
    import collections
    
    dictA = {'a':1, 'b':2, 'c':3}
    dictB = {'b':3, 'c':4, 'd':5}
    
    new_dict = collections.defaultdict(int)
    # use dict.items() instead of dict.iteritems() for Python3
    for k, v in itertools.chain(dictA.iteritems(), dictB.iteritems()):
        new_dict[k] += v
    
    print dict(new_dict)
    
    # OUTPUT
    {'a': 1, 'c': 7, 'b': 5, 'd': 5}
    

    OR

    Alternative you can use Counter as @Martijn has mentioned above.

    0 讨论(0)
  • 2020-11-22 02:33
    myDict = {}
    for k in itertools.chain(A.keys(), B.keys()):
        myDict[k] = A.get(k, 0)+B.get(k, 0)
    
    0 讨论(0)
  • 2020-11-22 02:33

    Additionally, please note a.update( b ) is 2x faster than a + b

    from collections import Counter
    a = Counter({'menu': 20, 'good': 15, 'happy': 10, 'bar': 5})
    b = Counter({'menu': 1, 'good': 1, 'bar': 3})
    
    %timeit a + b;
    ## 100000 loops, best of 3: 8.62 µs per loop
    ## The slowest run took 4.04 times longer than the fastest. This could mean that an intermediate result is being cached.
    
    %timeit a.update(b)
    ## 100000 loops, best of 3: 4.51 µs per loop
    
    0 讨论(0)
  • 2020-11-22 02:38

    Merging three dicts a,b,c in a single line without any other modules or libs

    If we have the three dicts

    a = {"a":9}
    b = {"b":7}
    c = {'b': 2, 'd': 90}
    

    Merge all with a single line and return a dict object using

    c = dict(a.items() + b.items() + c.items())
    

    Returning

    {'a': 9, 'b': 2, 'd': 90}
    
    0 讨论(0)
  • 2020-11-22 02:39

    Definitely summing the Counter()s is the most pythonic way to go in such cases but only if it results in a positive value. Here is an example and as you can see there is no c in result after negating the c's value in B dictionary.

    In [1]: from collections import Counter
    
    In [2]: A = Counter({'a':1, 'b':2, 'c':3})
    
    In [3]: B = Counter({'b':3, 'c':-4, 'd':5})
    
    In [4]: A + B
    Out[4]: Counter({'d': 5, 'b': 5, 'a': 1})
    

    That's because Counters were primarily designed to work with positive integers to represent running counts (negative count is meaningless). But to help with those use cases,python documents the minimum range and type restrictions as follows:

    • The Counter class itself is a dictionary subclass with no restrictions on its keys and values. The values are intended to be numbers representing counts, but you could store anything in the value field.
    • The most_common() method requires only that the values be orderable.
    • For in-place operations such as c[key] += 1, the value type need only support addition and subtraction. So fractions, floats, and decimals would work and negative values are supported. The same is also true for update() and subtract() which allow negative and zero values for both inputs and outputs.
    • The multiset methods are designed only for use cases with positive values. The inputs may be negative or zero, but only outputs with positive values are created. There are no type restrictions, but the value type needs to support addition, subtraction, and comparison.
    • The elements() method requires integer counts. It ignores zero and negative counts.

    So for getting around that problem after summing your Counter you can use Counter.update in order to get the desire output. It works like dict.update() but adds counts instead of replacing them.

    In [24]: A.update(B)
    
    In [25]: A
    Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1})
    
    0 讨论(0)
  • 2020-11-22 02:42
    def merge_with(f, xs, ys):
        xs = a_copy_of(xs) # dict(xs), maybe generalizable?
        for (y, v) in ys.iteritems():
            xs[y] = v if y not in xs else f(xs[x], v)
    
    merge_with((lambda x, y: x + y), A, B)
    

    You could easily generalize this:

    def merge_dicts(f, *dicts):
        result = {}
        for d in dicts:
            for (k, v) in d.iteritems():
                result[k] = v if k not in result else f(result[k], v)
    

    Then it can take any number of dicts.

    0 讨论(0)
提交回复
热议问题