Here is my solution code in Python 3 for the general case.
First I wrote a merge function, and then expanded it to a more general merge_with function, which takes a function and a different number of dictionaries. If there are duplicate keys in these dictionaries, apply the provided function to values ββwhose keys are duplicated.
The merge function can be overridden using the merge_with function, as in the case of the merge function. Merging names means merging them all and preserving the rightmost values, if there are duplicates. The merge function, which preserves the leftmost, does the same thing.
All functions here - merge, merge_with, mergel and merger - are common if they accept an arbitrary number of dictionary arguments. In particular, merge_with must take as an argument a function compatible with the data to which it will be applied.
from functools import reduce from operator import or_ def merge(*dicts): return { k: reduce(lambda d, x: x.get(k, d), dicts, None) for k in reduce(or_, map(lambda x: x.keys(), dicts), set()) } def merge_with(f, *dicts): return { k: (lambda x: f(*x) if len(x)>1 else x[0])([ d[k] for d in dicts if k in d ]) for k in reduce(or_, map(lambda x: x.keys(), dicts), set()) } mergel = lambda *dicts: merge_with(lambda *x: x[0], *dicts) merger = lambda *dicts: merge_with(lambda *x: x[-1], *dicts)
Test
>>> squares = { k:k*k for k in range(4) } >>> squares {0: 0, 1: 1, 2: 4, 3: 9} >>> cubes = { k:k**3 for k in range(2,6) } >>> cubes {2: 8, 3: 27, 4: 64, 5: 125} >>> merger(squares, cubes) {0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125} >>> merger(cubes, squares) {0: 0, 1: 1, 2: 4, 3: 9, 4: 64, 5: 125} >>> mergel(squares, cubes) {0: 0, 1: 1, 2: 4, 3: 9, 4: 64, 5: 125} >>> mergel(cubes, squares) {0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125} >>> merge(squares, cubes) {0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125} >>> merge(cubes, squares) {0: 0, 1: 1, 2: 4, 3: 9, 4: 64, 5: 125} >>> merge_with(lambda x, y: x+y, squares, cubes) {0: 0, 1: 1, 2: 12, 3: 36, 4: 64, 5: 125} >>> merge_with(lambda x, y: x*y, squares, cubes) {0: 0, 1: 1, 2: 32, 3: 243, 4: 64, 5: 125}
Update
After I wrote above, I found there another way to do this.
from functools import reduce def merge(*dicts): return reduce(lambda d1, d2: reduce(lambda d, t: dict(list(d.items())+[t]), d2.items(), d1), dicts, {}) def merge_with(f, *dicts): return reduce(lambda d1, d2: reduce(lambda d, t: dict(list(d.items()) + [(t[0], f(d[t[0]], t[1]) if t[0] in d else t[1])]), d2.items(), d1), dicts, {}) mergel = lambda *dicts: merge_with(lambda x, y: x, *dicts) merger = lambda *dicts: merge_with(lambda x, y: y, *dicts)
Note that merge and merge definitions using merge_with have been changed with the new functions as the first arguments. Function f should now be binary. The above tests still work. Here are some more tests to show the generality of these functions.
>>> merge() == {} True >>> merge(squares) == squares True >>> merge(cubes) == cubes True >>> mergel() == {} True >>> mergel(squares) == squares True >>> mergel(cubes) == cubes True >>> merger() == {} True >>> merger(squares) == squares True >>> merger(cubes) == cubes True >>> merge_with(lambda x, y: x+y, squares, cubes, squares) {0: 0, 1: 2, 2: 16, 3: 45, 4: 64, 5: 125} >>> merge_with(lambda x, y: x*y, squares, cubes, squares) {0: 0, 1: 1, 2: 128, 3: 2187, 4: 64, 5: 125}