I have two dictionaries
a = {'123': {'player': 1,
'opponent': 2},
'18': {'player': 10,
'opponent': 12}
}
b = {'123': {'winner': 1},
'180': {'winner': 2}
}
The goal is to merge them together and get one dictionary that looks as follows:
{'123': {'player': 1,
'opponent': 2,
'winner': 1},
'18': {'player': 10,
'opponent': 12},
'180': {'winner': 2}
}
I would like to use collections.ChainMap
for this purpose. I tried the following
>>> from collections import ChainMap
>>> print(dict(ChainMap(a, b)))
{'123': {'player': 1, 'opponent': 2}, '180': {'winner': 2}, '18': {'player': 10, 'opponent': 12}}
In the docs they create a new class as follows:
class DeepChainMap(ChainMap):
'Variant of ChainMap that allows direct updates to inner scopes'
def __setitem__(self, key, value):
for mapping in self.maps:
if key in mapping:
mapping[key] = value
return
self.maps[0][key] = value
def __delitem__(self, key):
for mapping in self.maps:
if key in mapping:
del mapping[key]
return
raise KeyError(key)
>>> print(dict(DeepChainMap(a, b)))
{'123': {'player': 1, 'opponent': 2}, '180': {'winner': 2}, '18': {'player': 10, 'opponent': 12}}
How can I modify the class and get the desired output?
You can create your own DeepChainMap
with a __getitem__
method that recursively constructs a DeepChainMap
object from all mappings with the given key if the value of the first mapping that has the given key is a dict:
from collections import ChainMap
class DeepChainMap(ChainMap):
def __getitem__(self, key):
values = (mapping[key] for mapping in self.maps if key in mapping)
try:
first = next(values)
except StopIteration:
return self.__missing__(key)
if isinstance(first, dict):
return self.__class__(first, *values)
return first
def __repr__(self): # convert to dict for a cleaner representation
return repr(dict(self))
so that:
print(DeepChainMap(a, b))
outputs:
{'123': {'winner': 1, 'player': 1, 'opponent': 2}, '180': {'winner': 2}, '18': {'player': 10, 'opponent': 12}}