ActiveState Code

Recipe 576473: mergeable_dict


A very simple specialization of the dictionary that makes it possible to merge two dictionaries provided that they are compatible : if a given key is present in both dictionaries, it must be associated with the same value.

The method is_compatible_with() allows to check this compatibility.

If possible, dictionaries can then be merged with the merge() method or with the '|' operator. Moreover, '|=' is defined as syntactical sugar for the merge() method.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# mergeable_dict - dict with merge() method.
#
# Author: Jerome Lovy

class mergeable_dict(dict):
    """dict with merge() method."""

    def is_compatible_with(self, other):
        for key in self:
            if key in other and self[key] != other[key]:
                return False
        return True

    def merge(self, other):
        for key in other:
            if key in self:
                if self[key] != other[key]:
                    raise ValueError
            else:
                self[key] = other[key]
        return self

    def __ior__(self, other):
        return self.merge(other)

    def __or__(self, other):
        result = mergeable_dict(self)
        for key in other:
            if key in result:
                if result[key] != other[key]:
                    raise ValueError
            else:
                result[key] = other[key]
        return result

Discussion

Semantics : - The merged dictionary has all the keys (union) of the initial dictionaries. - A key that is present in both initial dictionaries must be associated with the same value : trying to merge dictionaries which possess the same key with two different values will raise an exception.

Comments

  1. 1. At 1:25 a.m. on 7 sep 2008, Frank P Mora said:

    A one liner that, I think, does the same thing

    >>> d # dictionary 1
    {'a': 1, 'c': 3, 'b': 2}
    >
    >>> e # dictionary 2 with {'c': 3} also
    {'c': 3, 'e': 5, 'd': 4}
    >>>
    >>> # the one liner
    >>> dict(tuple(set(d.items()+e.items())))
    {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}
    >>>
    >>> # a function
    >>> f= lambda a,b: dict(tuple(set(a.items()+b.items())))
    >>> f(d,e) # function application
    {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}
    
  2. 2. At 10 p.m. on 14 sep 2008, Jérôme Lovy (the author) said:

    Thanks, Frank.

    It is worth noting though that given incompatible key/value pairs, dict will liberately take the last value provided:

    >>> my_d = {'a': 1, 'c': 3, 'b': 2}
    >>> my_e = {'c': 0, 'e': 5, 'd': 4}
    >>> tuple(set(my_d.items()+my_e.items()))
    (('a', 1), ('b', 2), ('e', 5), ('d', 4), ('c', 0), ('c', 3))
    >>> dict(tuple(set(my_d.items()+my_e.items())))
    {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}
    

    In my use-case, I prefer to have an exception raised.

Sign in to comment