0

I have four nested dictionaries:

    a = {
        1: {'a': 123, 'b': 222, 'c': 888},
        2: {'a': 333, 'b': 555, 'c': 345}}

    b = {
        1: {'d': 456, 'e': 333, 'f': 333},
        2: {'d': 555, 'e': 233, 'f': 433}}

    c = {
        1: {'g': 789, 'h': 444, 'i': 999},
        2: {'g': 456, 'h': 333, 'i': 333}}

    d = {
        1: {'j': 111, 'k': 555, 'l': 222},
        2: {'j': 456, 'k': 333, 'l': 333, 'm': 555}}

I want to concatenate/merge them into a new dictionary and group by the main key. The desired output is:

result = {
        1: {'a': 123, 'b': 222, 'c': 888, 'd': 456, 'e': 333, 'f': 333, 'g': 789, 'h': 444, 'i': 999, 'j': 111, 'k': 555, 'l': 222},
        2: {'a': 333, 'b': 555, 'c': 345, 'd': 555, 'e': 233, 'f': 433, 'g': 456, 'h': 333, 'i': 333, 'j': 456, 'k': 333, 'l': 333, 'm': 555}}

Each dictionary (a,b,c,d) has exactly 200 main keys (1 to 200) - I've only shown 2 entries each. There are no duplicate sub keys, but there are sometimes more subkeys in one than another (see "d").

I realize there are numerous very similar questions on StackOverflow, but the ones I found seem to involve something else (tuples, lists, combining only two dictionaries, etc.) and I have not been able to adapt any to my situation. An unsuccessful attempt (based on a similar question) that seemed logical given what I want the result to be:

result = {key: a[key] + b[key] + c[key] + d[key] for key in a}
BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33
Dennis
  • 269
  • 1
  • 13

5 Answers5

3

You can use reduce() with defaultdict() to build up the result. For each key in the (outer) dictionary, we accumulate the key-value pairs corresponding to the inner dictionary (i.e. dictionary that a key maps to) using reduce():

from functools import reduce
from collections import defaultdict

dictionaries = [a, b, c, d]

# Can use dict.__or__ in Python 3.9+, rather than dict(**x[k], **y[k])
result = reduce(
    lambda x, y: {k: dict(**x[k], **y[k]) for k in y},
    dictionaries, defaultdict(dict)
)
print(result)

This outputs:

{
 1: {'a': 123, 'b': 222, 'c': 888, 'd': 456, 'e': 333, 'f': 333, 'g': 789, 'h': 444, 'i': 999, 'j': 111, 'k': 555, 'l': 222},
 2: {'a': 333, 'b': 555, 'c': 345, 'd': 555, 'e': 233, 'f': 433, 'g': 456, 'h': 333, 'i': 333, 'j': 456, 'k': 333, 'l': 333, 'm': 555}
}
BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33
  • Do you know if your way is faster than, say, Dejean T's answer below?. – Dennis Jun 25 '22 at 18:21
  • You'd have to benchmark it, but it's important to note that their answer isn't extensible to more than four dictionaries (you could write them out by hand, but it'd be a massive pain). – BrokenBenchmark Jun 25 '22 at 18:23
1

Use the | operator to combine all the sub-dictionaries together:

import functools

result = {
    main_key: functools.reduce(
        dict.__or__,
        (sub_dicts[main_key] for sub_dicts in (a, b, c, d))
    )
    for main_key in a
}
Samwise
  • 68,105
  • 3
  • 30
  • 44
1

This is also an alternative way to solve your problem

dd = {}

for k in a:
    dd[k] = {**a[k], **b[k], **c[k], **d[k]}

Output:

{1: {'a': 123,
  'b': 222,
  'c': 888,
  'd': 456,
  'e': 333,
  'f': 333,
  'g': 789,
  'h': 444,
  'i': 999,
  'j': 111,
  'k': 555,
  'l': 222},
 2: {'a': 333,
  'b': 555,
  'c': 345,
  'd': 555,
  'e': 233,
  'f': 433,
  'g': 456,
  'h': 333,
  'i': 333,
  'j': 456,
  'k': 333,
  'l': 333,
  'm': 555}}
Dejene T.
  • 973
  • 8
  • 14
0

You can try using ** to merge multiple dicts together.

a = {
    1: {'a': 123, 'b': 222, 'c': 888},
    2: {'a': 333, 'b': 555, 'c': 345}}

b = {
    1: {'d': 456, 'e': 333, 'f': 333},
    2: {'d': 555, 'e': 233, 'f': 433}}

c = {
    1: {'g': 789, 'h': 444, 'i': 999},
    2: {'g': 456, 'h': 333, 'i': 333}}

d = {
    1: {'j': 111, 'k': 555, 'l': 222},
    2: {'j': 456, 'k': 333, 'l': 333, 'm': 555}}

result = {}
for i in range(1,len(a)+1):
    l = {**a[i], **b[i], **c[i], **d[i]}
    result[i] = l
    
print(result)
"""
{1: {'a': 123, 'b': 222, 'c': 888, 'd': 456, 'e': 333, 'f': 333, 'g': 789, 'h': 444, 'i': 999, 'j': 111, 'k': 555, 'l': 222}, 
 2: {'a': 333, 'b': 555, 'c': 345, 'd': 555, 'e': 233, 'f': 433, 'g': 456, 'h': 333, 'i': 333, 'j': 456, 'k': 333, 'l': 333, 'm': 555}}
"""
Ritwick Jha
  • 340
  • 2
  • 10
0

you can use simply the or operation on dictionary

a =

 {1: {'a': 123, 'b': 222, 'c': 888},
    2: {'a': 333, 'b': 555, 'c': 345}}

b =

 {1: {'d': 456, 'e': 333, 'f': 333},
    2: {'d': 555, 'e': 233, 'f': 433}}

c =

 {1: {'g': 789, 'h': 444, 'i': 999},
    2: {'g': 456, 'h': 333, 'i': 333}}

d =

 {1: {'j': 111, 'k': 555, 'l': 222},
    2: {'j': 456, 'k': 333, 'l': 333, 'm': 555}}

result =

  {1: a[1] | b[1] | c[1] | d[1],
    2: a[2] | b[2] | c[2] | d[2]}

print(result)

output :

{1:

{'a': 123, 'b': 222, 'c': 888, 'd': 456, 'e': 333, 'f': 333, 'g': 789, 'h': 444, 'i': 999, 'j': 111, 'k': 555, 'l': 222},2: 
  {'a': 333, 'b': 555, 'c': 345, 'd': 555, 'e': 233, 'f': 433, 'g': 456, 'h':333, 'i': 333, 'j': 456, 'k': 333, 'l': 333, 'm': 555}

}