Python
Dictionaries in Python
Data Structures
Dictionaries are an incredible data structure, that can assist in making your code more approachable, consistent and efficient.
Unfortunately, out of the box Python only provides a 1-dimensional dictionary. That being a dictionary with a single key. Technically, this can be extended to any number of keys using tuples as your key data type, but there is one potential flaw. That being, that the order of your tuple matters, meaning two keys existing in a dictionary may not be access the correct value if they are not in the correct order.
In your problem solving endeavours, you may encounter problems where you would like to access a particular value, using the speed of a dictionary, but with two or more keys. Where the order of those keys is not guaranteed to be consistent.
To do this I’ve provided code snippets for two custom dictionary classes:
TwoKeyDict
NKeyDict
The TwoKeyDict
class has the following methods:
get_value_by_key
→ returns the value stored for a given two key combination, but if there is no value for that combination it returns None
.set_key_value_pair
→ stores a new value or updates an existing one, for a given two key combination.remove_key_value_pair
→ deletes the key-value pair using the given two key combination, if it exists in the dictionary.class TwoKeyDict:
def __init__(self):
self.dictionary = {}
def get_value_by_key(self, key1, key2):
if (key1, key2) in self.dictionary:
return self.dictionary[(key1, key2)]
if (key2, key1) in self.dictionary:
return self.dictionary[(key2, key1)]
return None
def set_key_value_pair(self, key1, key2, value):
if (key2, key1) in self.dictionary:
self.dictionary[(key2, key1)] = value
else:
self.dictionary[(key1, key2)] = value
def remove_key_value_pair(self, key1, key2):
if (key1, key2) in self.dictionary:
del self.dictionary[(key1, key2)]
elif (key2, key1) in self.dictionary:
del self.dictionary[(key2, key1)]
else:
return None
example_dict = TwoKeyDict()
example_dict.set_key_value_pair("a", "b", 1)
print(example_dict.get_value_by_key("a", "b")) # 1
example_dict.set_key_value_pair("b", "a", 2)
print(example_dict.get_value_by_key("a", "b")) # 2
example_dict.set_key_value_pair("c", "d", 3)
print(
example_dict.get_value_by_key("c", "d"), example_dict.get_value_by_key("d", "c")
) # 3 3
print(
example_dict.get_value_by_key("a", "c"), example_dict.get_value_by_key("q", "x")
) # None None
example_dict.remove_key_value_pair("a", "b")
print(example_dict.get_value_by_key("a", "b")) # None
The NKeyDict
class has the following methods:
__init__
→ the constructor used to set the number of keys/dimensions (num_keys
) you would like the dictionary to use for key-value storage.get_value_by_key
→ returns the value stored for a given n-key combination, but if there is no value for that combination it returns None
.set_key_value_pair
→ stores a new value or updates an existing one, for a given n-key combination.remove_key_value_pair
→ deletes the key-value pair using the given n-key combination, if it exists in the dictionary.class NKeyDict:
def __init__(self, num_keys: int):
self.dictionary = {}
self.num_keys = num_keys
def get_value_by_key(self, keys: list[any]):
if len(keys) != self.num_keys:
raise Exception(
"Number of keys does not match the number of keys in the dictionary"
)
keys.sort()
return self.dictionary.get(tuple(keys), None)
def set_key_value_pair(self, keys: list[any], value):
if len(keys) != self.num_keys:
raise Exception(
"Number of keys does not match the number of keys in the dictionary"
)
keys.sort()
self.dictionary[tuple(keys)] = value
def remove_key_value_pair(self, keys: list[any]):
if len(keys) != self.num_keys:
raise Exception(
"Number of keys does not match the number of keys in the dictionary"
)
keys.sort()
if tuple(keys) in self.dictionary:
del self.dictionary[tuple(keys)]
else:
return None
A important difference in the implementation of TwoKeyDict
and NKeyDict
is that TwoKeyDict
takes each key as seperate arguments and checks for each permutation (ordered combination) of tuples in the dictionary. While NKeyDict
takes a list
of keys and sorts them so that permutations of the collection of keys don’t need to be checked.
example_3d_dict = NKeyDict(3)
example_3d_dict.set_key_value_pair([1, 2, 3], "Hello World!")
print(
example_3d_dict.get_value_by_key([1, 2, 3]),
example_3d_dict.get_value_by_key([3, 2, 1]),
) # Hello World! Hello World!
example_3d_dict.set_key_value_pair([2, 3, 1], "Goodbye!")
print(example_3d_dict.get_value_by_key([1, 2, 3])) # Goodbye!
example_3d_dict.remove_key_value_pair([1, 2, 3])
print(example_3d_dict.get_value_by_key([1, 2, 3])) # None
example_3d_dict.set_key_value_pair([5, 6, 7, 8], "This will cause an error!")
# Exception: Number of keys does not match the number of keys in the dictionary
Using the above code snippets, you can implement easily implement and utilise a multi-key dictionary in your programming tasks.
If you found this article helpful, check out my profile for other articles and follow me for more articles like this.