I would like to merge two dictionaries A and B, knowing a common previous state C of both dictionaries. I need the merge to occur in subdictionaries too. In the case of real conflict, I need an exception to be raised.
1 - In the following example, the merge method should understand that A and B edited different items, and thus the merge should not raise a conflict
C = {"x": 0, "y": 0}
A = {"x": 1, "y": 0} # Edit x, but not y
B = {"x": 0, "y": 1} # Edit y, but not x
# merge(A, B, C) => {"x": 1, "y": 1}
2 - The function needs to be able to deal with new items and deleted items
C = {"x": 0}
A = {"x": 0, "y": 0} # Add y, keep x untouched
B = {} # Delete x
# merge(A, B, C) => {"y": 0}
3 - The function should raise an exception when a real conflict occurs
C = {"x": 0}
A = {"x": 1} # Edit x
B = {"x": 2} # Also edit x
# merge(A, B, C) => raise Exception
C = {"x": 0}
A = {"x": 1} # Edit x
B = {} # Delete x
# merge(A, B, C) => raise Exception
4 - The function should work recursively
C = {"deeper": {"x": 0, "y": 0}}
A = {"deeper": {"x": 1, "y": 0}} # Edit deeper["x"], but not deeper["y"]
B = {"deeper": {"x": 0, "y": 1}} # Edit deeper["y"], but not deeper["x"]
# merge(A, B, C) => {"deeper": {"x": 1, "y": 1}}
What's the best approach to implement such a merge function?
You can convert all the dict items into sets, use the intersection of keys from the symmetric differences to C to find conflicts, and use a union of the 3 sets' intersection (common items) and the differences to C to obtain the merge. Recursively merge sub-dicts that are common to A, B and C, convert sub-dicts into tuples of item pairs to allow them to be hashable and convertible into sets, and then convert them back to dicts after merge.
EDIT: In case the dict values are unhashable objects such as a set, you would have to serialize the values (I recommend using
pickle
as a serializer since it has native support from Python) before you can convert the dict items into a set, and de-serialize them after the merge:so that:
would output:
while:
would raise:
and:
would raise: