Python dict is a very useful data-structure:
d = {'a': 1, 'b': 2}
d['a'] # get 1
Sometimes you'd also like to index by values.
d[1] # get 'a'
Which is the most efficient way to implement this data-structure? Any official recommend way to do it?
Here is a class for a bidirectional dict, inspired by Finding key from value in Python dictionary and modified to allow the following 2) and 3).
Note that :
bd.inverse auto-updates itself when the standard dict bd is modified.bd.inverse[value] is always a list of key such that bd[key] == value.bidict module from https://pypi.python.org/pypi/bidict, here we can have 2 keys having same value, this is very important.Code:
class bidict(dict):
    def __init__(self, *args, **kwargs):
        super(bidict, self).__init__(*args, **kwargs)
        self.inverse = {}
        for key, value in self.items():
            self.inverse.setdefault(value, []).append(key) 
    def __setitem__(self, key, value):
        if key in self:
            self.inverse[self[key]].remove(key) 
        super(bidict, self).__setitem__(key, value)
        self.inverse.setdefault(value, []).append(key)        
    def __delitem__(self, key):
        self.inverse.setdefault(self[key], []).remove(key)
        if self[key] in self.inverse and not self.inverse[self[key]]: 
            del self.inverse[self[key]]
        super(bidict, self).__delitem__(key)
Usage example:
bd = bidict({'a': 1, 'b': 2})  
print(bd)                     # {'a': 1, 'b': 2}                 
print(bd.inverse)             # {1: ['a'], 2: ['b']}
bd['c'] = 1                   # Now two keys have the same value (= 1)
print(bd)                     # {'a': 1, 'c': 1, 'b': 2}
print(bd.inverse)             # {1: ['a', 'c'], 2: ['b']}
del bd['c']
print(bd)                     # {'a': 1, 'b': 2}
print(bd.inverse)             # {1: ['a'], 2: ['b']}
del bd['a']
print(bd)                     # {'b': 2}
print(bd.inverse)             # {2: ['b']}
bd['b'] = 3
print(bd)                     # {'b': 3}
print(bd.inverse)             # {2: [], 3: ['b']}
                        You can use the same dict itself by adding key,value pair in reverse order.
d={'a':1,'b':2}
revd=dict([reversed(i) for i in d.items()])
d.update(revd)
                        A poor man's bidirectional hash table would be to use just two dictionaries (these are highly tuned datastructures already).
There is also a bidict package on the index:
The source for bidict can be found on github:
The below snippet of code implements an invertible (bijective) map:
class BijectionError(Exception):
    """Must set a unique value in a BijectiveMap."""
    def __init__(self, value):
        self.value = value
        msg = 'The value "{}" is already in the mapping.'
        super().__init__(msg.format(value))
class BijectiveMap(dict):
    """Invertible map."""
    def __init__(self, inverse=None):
        if inverse is None:
            inverse = self.__class__(inverse=self)
        self.inverse = inverse
    def __setitem__(self, key, value):
        if value in self.inverse:
            raise BijectionError(value)
        self.inverse._set_item(value, key)
        self._set_item(key, value)
    def __delitem__(self, key):
        self.inverse._del_item(self[key])
        self._del_item(key)
    def _del_item(self, key):
        super().__delitem__(key)
    def _set_item(self, key, value):
        super().__setitem__(key, value)
The advantage of this implementation is that the inverse attribute of a BijectiveMap is again a BijectiveMap. Therefore you can do things like:
>>> foo = BijectiveMap()
>>> foo['steve'] = 42
>>> foo.inverse
{42: 'steve'}
>>> foo.inverse.inverse
{'steve': 42}
>>> foo.inverse.inverse is foo
True
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With