# Bi Directional Dictionary

I wrote a class that extends the dict class to provide indexing like functionality, so I can find which key(s) map to a given value in the dict. It's pretty bare bones and there are a lot of things I haven't fleshed out like docstrings (will add these later when I have a final version), how to handle unhashable elements (it currently just fails, but this might be changed in the future), etc.

The extremely bare bones nature of this is because this is something I whipped up in a few minutes for immediate use.

from collections import defaultdict  # The class currently doesn't exist in a separate module, and there are many other imports used by surrounding code.

# Do something

class DoubleDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.values_dict = defaultdict(lambda: [])
for key, value in self.items():
self.values_dict[value] += [key]

def __setitem__(self, key, value):
var = self.get(key)
if var in self.values_dict:
self.values_dict[var].remove(key)
if not self.values_dict[var]:
del self.values_dict[var]
super().__setitem__(key, value)
self.values_dict[value] += [key]

def index(self, value):
return self.values_dict[value][-1]

def indices(self, value):
return self.values_dict[value]

# Do Something



My concerns are to have this be as performant as feasible, and sane and generally correct (there may be some unavoidable tradeoffs) approach. I would probably make this into its own module after incorporating feedback.

• Why/how would you use this? Is this an alternative to turning them inside-out? – Mast Dec 14 '19 at 19:26
• For finding the keys that map to a given value. – Tobi Alafin Dec 14 '19 at 19:30
• Your latest edit invalidated part of my answer. And so I have rolled it back as per site rules. Lets not post incomplete questions, and then edit it whilst answerers are trying to answer you question. That's just bad form. – Peilonrayz Dec 14 '19 at 19:52

## 2 Answers

A couple of minor notes:

1) Add proper typing so that users of your code will be able to type-check their own code correctly. I think this would look like:

from typing import Dict, List, TypeVar

_KT = TypeVar('_KT')
_VT = TypeVar('_VT')

class DoubleDict(Dict[_KT, _VT]):
super().__init__(*args, **kwargs)
self.values_dict: Dict[_VT, List[_KT]] = defaultdict(lambda: [])
for key, value in self.items():
self.values_dict[value] += [key]

... etc


2) I'd eliminate the index syntactic sugar, personally, because it makes it easier for the caller to forget that this isn't a one-to-one mapping in both directions. Make them get the list and check to make sure that it's got the length they expect.

Alternatively, if you'd want it to be a one-to-one mapping in most cases, then enforce that in the setter and make it part of the class's contract.

• what does typing do? – Noone AtAll Dec 15 '19 at 7:55
• It makes you not hate your life when you're maintaining your code. – Sam Stafford Dec 15 '19 at 16:58
• see: mypy-lang.org If you're writing Python code without mypy you're going to have a bad time. – Sam Stafford Dec 15 '19 at 17:04

I don't see the point in this, further more if you want to hash unhashable things then you probably have a design problem.

Realistically this class lulls you into a false sense of security.

>>> dd = DoubleDict()
>>> dd[1] = 2
>>> dd.update({'foo': 'bar'})
>>> del dd[1]
>>> dict(dd.values_dict)
{2: [1]}
>>> dd
{'foo': 'bar'}


If you haven't implemented all edge cases don't inherit. Instead just make the class an interface. If "there are a lot of things I haven't fleshed out" then don't inherit. Some may argue just don't inherit period.

I think inheritance is good if you know how to use it. Like if you inherit collections.abc.MutableMapping. By defining a meager 5 methods you too can have a complete dictionary. Better yet, if you inherit from typing.MutableMapping you can have the object be fully typed too - if you type the abstract methods.

• I've edited my answer to reflect my current version of the class. (It properly updates the .values_dict upon item reassignment and deletion). (I should have probably fully fleshed it out more before making it a question). I would probably make a new question if I make significant further improvements. – Tobi Alafin Dec 14 '19 at 19:52