Skip to content

Instantly share code, notes, and snippets.

@Nazek42
Created August 12, 2017 23:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nazek42/87971a526ec045ba1e79cea99797746b to your computer and use it in GitHub Desktop.
Save Nazek42/87971a526ec045ba1e79cea99797746b to your computer and use it in GitHub Desktop.
Python two-way dictionary
# dedict, aka "double-ended dict"
# A data structure allowing for bidirectional lookups
#
# Syntax for lookups:
# By key:
# myDedict[key] OR myDedict[key:]
# By value:
# myDedict[:value]
# This is meant to evoke the dict literal syntax of key: value.
#
# Assignment works similarly:
# myDedict[key] = value
# myDedict[key:] = value
# myDedict[:value] = key
#
# The 'in' operator works as with a normal dict:
# key in myDedict
#
# The inversion operator (~) can be used to return a key-value swapped
# shallow copy of the dedict:
# myInvDedict = ~myDedict
#
# This can be used in conjuction with the 'in' operator:
# value in ~myDedict
# (equivalent to `value in myDedict.values()`)
class dedict:
def __init__(self, *args, **kwargs):
self._dict = dict(*args, **kwargs)
self._update_rdict()
def get_by_key(self, key):
return self._dict[key]
def get_by_value(self, value):
return self._rdict[value]
def set(self, key, value):
self[key] = value
def update(self, addition):
self._dict.update(addition)
self._update_rdict()
def keys(self):
return self._dict.keys()
def values(self):
return self._dict.values()
def items(self):
return self._dict.items()
def _update_dict(self):
self._dict = dict(zip(self._rdict.values(), self._rdict.keys()))
def _update_rdict(self):
self._rdict = dict(zip(self._dict.values(), self._dict.keys()))
def __len__(self):
return len(self._dict)
def __getitem__(self, key):
if isinstance(key, slice):
if key.start is None and key.step is None: # aka, if searching by value:
return self._rdict[key.stop]
elif key.stop is None and key.step is None: # aka, if searching by key:
return self._dict[key.start]
else: # malformed slice
raise TypeError("Malformed slice '{}'".format(key))
else: # obey default lookup behavior
return self._dict[key]
def __setitem__(self, key, value):
if isinstance(key, slice):
if key.start is None and key.step is None: # aka, if setting by value:
self._rdict[key.stop] = value
self._update_dict()
elif key.stop is None and key.step is None: # aka, if setting by key:
self._dict[key.start] = value
self._update_rdict()
else: # malformed slice
raise TypeError("Malformed slice '{}'".format(key))
else: # obey default lookup behavior
self._dict[key] = value
self._update_rdict()
def __delitem__(self, key):
if isinstance(key, slice):
if key.start is None and key.step is None: # aka, if searching by value:
del self._rdict[key.stop]
self._update_dict()
elif key.stop is None and key.step is None: # aka, if searching by key:
del self._dict[key.start]
self._update_rdict()
else: # malformed slice
raise TypeError("Malformed slice '{}'".format(key))
else: # obey default lookup behavior
del self._dict[key]
self._update_rdict()
def __iter__(self):
return self._dict.__iter__()
def __contains__(self, key):
return (key in self._dict)
def __invert__(self):
return dedict(self._rdict)
def __repr__(self):
return "dedict({})".format(self._dict)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment