views:

136

answers:

3

Python dict is a very useful datastructure:

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 datastructure? Any official recommend way to do it?

Thanks!

+5  A: 

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 bitbucket:

The MYYN
2 dicts requires double inserts and deletes.
Juanjo Conti
@Robuts, didn't understand you.
Juanjo Conti
@Juanjo: nearly any bidirectional/reversible hash table will involve "double inserts and deletes", either as part of implementing the structure, or as part of using it. Keeping two indexes is really the only fast way to do it, AFAIK.
Walter Mundt
@Walter Mundt: Agreed.
The MYYN
Of course; I meant that take care of the 2 index by hand is the problem.
Juanjo Conti
A: 

Something like this, maybe:

class BidirDict(dict):
    def __init__(self, iterable=(), **kwargs):
        self.update(iterable, **kwargs)
    def update(self, iterable=(), **kwargs):
        if hasattr(iterable, 'iteritems'):
            iterable = iterable.iteritems()
        for (key, value) in itertools.chain(iterable, kwargs.iteritems()):
            self[key] = value
    def __setitem__(self, key, value):
        if key in self:
            del self[key]
        if value in self:
            del self[value]
        dict.__setitem__(self, key, value)
        dict.__setitem__(self, value, key)
    def __delitem__(self, key):
        value = self[key]
        dict.__delitem__(self, key)
        dict.__delitem__(self, value)
    def __repr__(self):
        return '%s(%s)' % (type(self).__name__, dict.__repr__(self))

You have to decide what you want to happen if more than one key has a given value; the bidirectionality of a given pair could easily be clobbered by some later pair you inserted. I implemented one possible choice.

Matt Anderson
I'm not sure if this is a problem, but using the above implementation, wouldn't there be issues if the keys and values overlapped? So `dict([('a', 'b'), ('b', 'c')]); dict['b']` -> `'c'` instead of the key `'a'`.
tgray
It's not an issue for the OP's example, but might be a good disclaimer to include.
tgray
+1  A: 

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)
Emil
+1 A nice, practical solution. Another way to write it: `d.update( dict((d[k], k) for k in d) )`.
FM