0

I need to combine two dictionaries in python in such a way that values using the same keyS are added. I already found this answer based on collections.Counter, but this does not work on nested dictionaries.

One example:

A = {0: {1: 8, 2: 4}, 1: {0: 8, 2: 3}, 2: {0: 3, 1: 7}}
B = {0: {1: 1, 2: 0}, 1: {0: 1, 2: 5}, 2: {0: 4, 1: 10}}

Result should be:

combine(A,B) = {0: {1: 9, 2: 4}, 1: {0: 9, 2: 8}, 2: {0: 7, 1: 17}}

There are always two level of nesting and the key sets are always identical. If A[x][y] exists, you can assume that B[x][y] also exists, also the other way around. Both dicts are initialized with 0 entries. Thanks in advance!

Community
  • 1
  • 1
Daniel
  • 1,398
  • 4
  • 20
  • 47

3 Answers3

2

Building on the Counter approach:

combined = {k: Counter(A[k]) + Counter(B[k]) for k in A}

This works as long as A and B have the same top-level key-set; it's robust to differences in the second level keys.

tzaman
  • 46,925
  • 11
  • 90
  • 115
  • Thank you! Personally I'd prefer this approach, as it is shorter, in my opinion easier to read and works perfectly. – Daniel Sep 28 '15 at 22:57
1
def combine(a, b):
    """Combines two dictionaries of int-values through addition"""

    return {outer_k: {inner_k: a[outer_k][inner_k] + b[outer_k][inner_k] for
                      inner_k in a[outer_k]} for
            outer_k in a}

This is a very fragile solution, but since you specified as part of the question that all a[k] and a[k][kk] are in b and b[k], this should work great!

Adam Smith
  • 52,157
  • 12
  • 73
  • 112
  • The backslashes are not needed and I find it unusual to see the `for` at the end of the line, not at the beginning of the next one. – mkrieger1 Sep 28 '15 at 22:30
  • @mkrieger1 right about the backslashes -- I've been chaining `if`s lately! Per PEP8: "The preferred place to break around a binary operator is after the operator, not before it." `for` isn't a binary operator per-say, but I feel like it follows the same style. – Adam Smith Sep 28 '15 at 22:32
  • That's true, but I don't think `for` is a binary operator. – mkrieger1 Sep 28 '15 at 22:33
  • @mkrieger1 it's not, but that informs my decision. I amended my earlier commend. Call it a personal choice :) Either way if the only nitpicks on my code is style, I consider that a compliment! – Adam Smith Sep 28 '15 at 22:34
  • Thank you, works perfectly. I however prefer @tzaman approach, as it is shorter and in my opinion simpler to read – Daniel Sep 28 '15 at 22:57
  • @waza-ari No problem. The only issue with tzaman's approach is the slightly larger memory footprint (negligible with small data structures like this) and needing an extra import. Otherwise it works great! Beware that you then have a dict of `Counter`s, instead of a dict of dicts, though! – Adam Smith Sep 28 '15 at 23:19
  • @AdamSmith: Thank you for your feedback. Yes, an additional import is required. Data is a little larger than shown here, but still negligible. AFAIK a `Counter` is a subtype of a `dict`, so it should basically make no difference when doing things like iterating over it? – Daniel Sep 28 '15 at 23:23
  • @waza-ari correct. Certainly not a reason to avoid that style if you prefer the look of it – Adam Smith Sep 28 '15 at 23:25
0
def add_dicts(dd1, dd2):
    combD = {}
    for i in dd1.keys():
         combD[i] = dd1[i] + dd2[i]
    return combD

for k in A.keys():
    C[k] = add_dicts(A[k], B[k])

print(C)

{0: {1: 9, 2: 4}, 1: {0: 9, 2: 8}, 2: {0: 7, 1: 17}}
LetzerWille
  • 5,355
  • 4
  • 23
  • 26