2

I'd like to add the values of a dictionary to another dictionary. For example:

adict = {1: {'a': 13, 'b': 19, 'c': 15}, 2: {'a': 7, 'b': 2, 'c': 0}}

If we add {1: {'a': 3, 'b': 9, 'c': 23}} to adict

Then adict should now be:

{1: {'a': 16, 'b': 28, 'c': 38}, 2: {'a': 7, 'b': 2, 'c': 0}}

If we add {3: {'a': 4}} then adict should now be:

{1: {'a': 16, 'b': 28, 'c': 38}, 2: {'a': 7, 'b': 2, 'c': 0}, 3: {'a': 4}}

and if we add {2: {'a': 1, 'b': 8, 'c': 27, 'd': 11}}

Then adict should now be:

{1: {'a': 16, 'b': 28, 'c': 38}, 2: {'a': 8, 'b': 10, 'c': 27, 'd': 11}, 3: {'a': 4}}

What's the best way to do this?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Russell
  • 2,692
  • 5
  • 23
  • 24

3 Answers3

6

Simple recursive function:

>>> adict = {1: {'a': 13, 'b': 19, 'c':15}, 2: {'a': 7, 'b': 2, 'c':0}}
>>> def dict_add(a,b):
...   a = a.copy()
...   for k,v in b.items():
...     if isinstance(v,(dict,)):
...       a[k] = dict_add(a.get(k,{}),v)
...     else:
...       a[k] = a.get(k,0) + v
...   return a
...
>>> dict_add(adict,{1: {'a': 3, 'b': 9, 'c': 23}})
{1: {'a': 16, 'c': 38, 'b': 28}, 2: {'a': 7, 'c': 0, 'b': 2}}
>>> dict_add(dict_add(adict,{1: {'a': 3, 'b': 9, 'c': 23}}),{3:{'a':4}})
{1: {'a': 16, 'c': 38, 'b': 28}, 2: {'a': 7, 'c': 0, 'b': 2}, 3: {'a': 4}}
MattH
  • 37,273
  • 11
  • 82
  • 84
  • Heh, I ended up transforming my code into yours in the long run. +1 from me – Blender Dec 22 '11 at 16:31
  • My page hadn't refreshed, so I'd not seen your solution until after I'd posted mine. Demonstrated by how close both our answers are/were I think one of the major likeable things about python is the convergence of the expression of ideas... as opposed to the [alternative](http://en.wikipedia.org/wiki/There's_more_than_one_way_to_do_it) – MattH Dec 22 '11 at 16:36
  • With the loose typing, it's more of a logical flow of ideas, really. I can't imagine doing this in C++, though. – Blender Dec 22 '11 at 16:40
3

This is probably very inefficient, but here's what I came up with:

def dict_add(a, b):
  result = dict(a)

  for key, value in b.items():
    if type(value) != dict:
      result[key] = result.get(key, 0) + value
    else:
      result[key] = dict_add(result.get(key, {}), value)

  return result

Running this code results in this:

>>> adict = {1: {'a': 13, 'b': 19, 'c':15}, 2: {'a': 7, 'b': 2, 'c':0}}
>>> bdict = {1: {'a': 3, 'b': 9, 'c': 23}}
>>> 
>>> print dict_add(adict, bdict)
{1: {'a': 16, 'c': 38, 'b': 28}, 2: {'a': 7, 'c': 0, 'b': 2}}
Blender
  • 289,723
  • 53
  • 439
  • 496
0

Here is a functional solution. The rec_add function does what you ask with arbitrarily nested dictionaries.

def combine(f, d1, d2):
    """Create a new dict combining d1 and d2.

    Keys appearing only in one of the input dict are copied unmodified. Values
    with matching keys are combined using f and copied in the output dict."""

    keys = set(d1.keys() + d2.keys())
    out = { }
    for key in keys:
        if key in d1:
            if key in d2:
                out[key] = f(d1[key], d2[key])
            else:
                out[key] = d1[key]
        else:
            out[key] = d2[key]
    return out

def rec_combine(f, d1, d2):
    """Recursively combine all dicts."""

    def aux(v1, v2):
        if isinstance(v1, (dict,)) and isinstance(v2, (dict,)):
            return rec_combine(f, v1, v2)
        else:
            return f(v1, v2)

    return combine(aux, d1, d2)

def rec_add(d1, d2):
    """Recursively sum values in d1 and d2."""
    return rec_combine(lambda x, y: x + y, d1, d2)
Paolo Capriotti
  • 4,052
  • 21
  • 25