41

In Python, I have list of dicts:

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]

I want one final dict that will contain the sum of all dicts. I.e the result will be: {'a':5, 'b':7}

N.B: every dict in the list will contain same number of key, value pairs.

sth
  • 222,467
  • 53
  • 283
  • 367
Nazmul Hasan
  • 6,840
  • 13
  • 36
  • 37

11 Answers11

59

You can use the collections.Counter

counter = collections.Counter()
for d in dict1: 
    counter.update(d)

Or, if you prefer oneliners:

functools.reduce(operator.add, map(collections.Counter, dict1))
SiggyF
  • 22,088
  • 8
  • 43
  • 57
  • 1
    or `sum(map(collections.Counter, dict1),Counter())`. But I am not sure about the relative performance of the functional versions creating all those `Counters()` – John La Rooy Aug 16 '10 at 06:49
  • 7
    This answer demonstrates the golden rule of Python programming: if it comes included with Python don't reinvent the wheel. One point: the final result `counter` is an instance of a subclass of `dict`, if the OP wants a plain `dict` he might add a final `counter = dict(counter)`. – Duncan Aug 16 '10 at 07:51
  • If the dicts do not all have the same keys, the first solution will output the result with only the keys shared between all dicts, while the second one-liner solution will output the result with all keys (treating missing keys as value 0) – roob May 22 '17 at 23:55
18

A little ugly, but a one-liner:

dictf = reduce(lambda x, y: dict((k, v + y[k]) for k, v in x.iteritems()), dict1)
carl
  • 49,756
  • 17
  • 74
  • 82
  • actually i have a object list and this dictionary is an object property, have any solution? :( – Nazmul Hasan Aug 16 '10 at 05:28
  • 1
    [ob1, ob2, ob3].. each object has a property data ob1.data it returns a dict {'a':2, 'b':3} like that – Nazmul Hasan Aug 16 '10 at 05:33
  • 1
    `dictf = reduce(lambda x, y: dict((k, v + y.data[k]) for k, v in x.data.iteritems()), dict1` – carl Aug 16 '10 at 05:35
  • my code working by this solution ..ok...reduce(lambda x, y: dict((k, v + y.get_local_expenses()[k] if not isinstance(y.get_local_expenses()[k], dict) else 0) for k, v in x.get_local_expenses().iteritems()), glc) – Nazmul Hasan Aug 16 '10 at 05:49
  • 1
    @nazmul hasan: Will you understand that in 6 months time? You have written get_local_expenses() 3 times -- is that necessary? What is glc? Have you read @paxdiablo's answer? – John Machin Aug 16 '10 at 06:44
  • Perhaps the only good thing about my answer is that it utilizes some cool concepts: reduce, lambda, generator expressions, and iterators. – carl Aug 16 '10 at 06:54
  • @John Machin: glc is a list of object get_local_expense() is a property of each object and return a dictionary dictionary value may be another dict or str value or deciamal value.....i have read the @paxdiablo's ans and i solved it before him myself but i like one-liner solution :) – Nazmul Hasan Aug 16 '10 at 06:57
13

Leveraging sum() should get better performance when adding more than a few dicts

>>> dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]
>>> from operator import itemgetter
>>> {k:sum(map(itemgetter(k), dict1)) for k in dict1[0]}        # Python2.7+
{'a': 5, 'b': 7}
>>> dict((k,sum(map(itemgetter(k), dict1))) for k in dict1[0])  # Python2.6
{'a': 5, 'b': 7}

adding Stephan's suggestion

>>> {k: sum(d[k] for d in dict1) for k in dict1[0]}            # Python2.7+
{'a': 5, 'b': 7}
>>> dict((k, sum(d[k] for d in dict1)) for k in dict1[0])      # Python2.6
{'a': 5, 'b': 7}

I think Stephan's version of the Python2.7 code reads really nicely

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • 2
    Is there any reason you use `map` and `itemgetter` instead of list comprehension in the inner loop (i.e. `dict((k, sum(d[k] for d in dict1)) for k in dict1[0])`)? – stephan Aug 16 '10 at 07:39
  • @stephan, It used to be faster..seems to be about the same speed now. I'll add it to my answer – John La Rooy Aug 16 '10 at 08:04
  • A nice addition to this version that also checks the dict types making sure we can do mathematical operations on top of them: `{k: sum(d[k] if type(d[k]) in (int, float) else 0 for d in dict1) for k in dict1[0]}` – ygbr Mar 19 '13 at 21:04
10

This might help:

def sum_dict(d1, d2):
    for key, value in d1.items():
        d1[key] = value + d2.get(key, 0)
    return d1

>>> dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]
>>> reduce(sum_dict, dict1)
{'a': 5, 'b': 7}
Manoj Govindan
  • 72,339
  • 21
  • 134
  • 141
5

I was interested in the performance of the proposed Counter, reduce and sum methods for large lists. Maybe someone else is interested in this as well. You can have a look here: https://gist.github.com/torstenrudolf/277e98df296f23ff921c

I tested the three methods for this list of dictionaries:

dictList = [{'a': x, 'b': 2*x, 'c': x**2} for x in xrange(10000)]

the sum method showed the best performance, followed by reduce and Counter was the slowest. The time showed below is in seconds.

In [34]: test(dictList)
Out[34]: 
{'counter': 0.01955194902420044,
 'reduce': 0.006518083095550537,
 'sum': 0.0018319153785705566}

But this is dependent on the number of elements in the dictionaries. the sum method will slow down faster than the reduce.

l = [{y: x*y for y in xrange(100)} for x in xrange(10000)]

In [37]: test(l, num=100)
Out[37]: 
{'counter': 0.2401433277130127,
 'reduce': 0.11110662937164306,
 'sum': 0.2256883692741394}
trudolf
  • 1,809
  • 16
  • 11
5

The following code shows one way to do it:

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]

final = {}
for k in dict1[0].keys():           # Init all elements to zero.
    final[k] = 0
for d in dict1:
    for k in d.keys():
        final[k] = final[k] + d[k]  # Update the element.

print final

This outputs:

{'a': 5, 'b': 7}

as you desired.

Or, as inspired by kriss, better but still readable:

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]

final = {}
for d in dict1:
    for k in d.keys():
        final[k] = final.get(k,0) + d[k]

print final

I pine for the days of the original, readable Python :-)

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • You can simplify the first `for` loop to `final={}.fromkeys(dict1[0],0)`. or is that what "readable" is getting at? :) – John La Rooy Aug 16 '10 at 06:41
  • I could _simplify_ the whole thing into carl's answer but that would mean (1) I may as well delete my answer; and (2) I wouldn't be able to read it next month when I found I needed a small change :-) I should mention that I use Python for teaching (_my_ brand of Python rather than carl's brand). It really is a good language for teaching the basics (sequence, iteration, selection) to kids but, if you're going to whack them over the head with lambdas and such, you may as well teach them F# or Haskell. – paxdiablo Aug 16 '10 at 06:47
  • @paxdiablo: for readability, you can fully remove the init loop, just replace `+ d[k]` with `+ res.get(k, 0)` – kriss Aug 16 '10 at 06:58
  • That's a good one, @kriss, and still very readable but I think you meant replace `final[k] + d[k]` with `final.get(k,0) + d[k]` - it's the `final` dictionary that I was requiring a default value from if the key didn't exist - I _know_ it esists for `d`. – paxdiablo Aug 16 '10 at 07:16
  • @paxdiablo: oups! Yes, your are absolutely right, I inverted dictionnaries. – kriss Aug 16 '10 at 08:13
  • @paxdiablo: you may even go one step further (but I wonder for readibality, see by yourself). Replace the inner loop with `final = dict((k, v + final.get(k, 0)) for k, v in d.iteritems())` – kriss Aug 16 '10 at 08:16
  • Now it's just starting to look like "Είναι ένα, ένα χιλιόμετρο μακρινός από εδώ" as in useful, but not overly comprehensible (unless you're steeped in the language). – paxdiablo Aug 16 '10 at 09:46
4

You can also use the pandas sum function to compute the sum:

import pandas as pd
# create a DataFrame
df = pd.DataFrame(dict1)
# compute the sum and convert to dict.
dict(df.sum())

This results in:

{'a': 5, 'b': 7}

It also works for floating points:

dict2 = [{'a':2, 'b':3.3},{'a':3, 'b':4.5}]
dict(pd.DataFrame(dict2).sum())

Gives the correct results:

{'a': 5.0, 'b': 7.8}
SiggyF
  • 22,088
  • 8
  • 43
  • 57
2

Here is a reasonable beatiful one.

final = {}
for k in dict1[0].Keys():
    final[k] = sum(x[k] for x in dict1)
return final
Kyan
  • 383
  • 3
  • 7
2

In Python 2.7 you can replace the dict with a collections.Counter object. This supports addition and subtraction of Counters.

Dave Kirby
  • 25,806
  • 5
  • 67
  • 84
1

Here is another working solution (python3), quite general as it works for dict, lists, arrays. For non-common elements, the original value will be included in the output dict.

def mergsum(a, b):
    for k in b:
        if k in a:
            b[k] = b[k] + a[k]
    c = {**a, **b}
    return c

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]
print(mergsum(dict1[0], dict1[1]))
Ste
  • 43
  • 7
0

One further one line solution

dict(
    functools.reduce(
        lambda x, y: x.update(y) or x,  # update, returns None, and we need to chain.
        dict1,
        collections.Counter())
)

This creates only one counter, uses it as an accumulator and finally converts back to a dict.

Aaron McMillin
  • 2,532
  • 27
  • 42