0

I wish to have a dictionary that is interchangeable i.e. values are mapped with keys & keys are mapped with value so that if we know one element, the other partner can be known. In python dict, using keys, we can get values but not vice-versa. How can I achieve this? Note: For interchangeability, there has to be a 1-1 mapping between key: value & value: key.

Example:

key = {'a':'1','b':'2','c':'3'}
So, I should be able to get 1 for key['a'] & 'a' for key['1']
martineau
  • 119,623
  • 25
  • 170
  • 301
Mehul Gupta
  • 1,829
  • 3
  • 17
  • 33
  • You could use a paire of dictionaries back to back but you would have to write all your update operations in duplicate. It would be heavy. It would be simpler to check whether the value exists before each insert. See this post for getting key from value: https://stackoverflow.com/questions/1383797/java-hashmap-how-to-get-key-from-value –  Feb 12 '22 at 12:03

3 Answers3

1

One possible way to that is... you can simply updated existing dictionary as it contains all the opposite key-value pairs, as follows:

dict1 = {'a': '1','b' :'2','c': '3'}
dict2 = dict(zip(dict1.values(), dict1.keys())) # create value-key pair dictionary

dict1.update(dict2) # combining dict2 with dict1
print(dict1)
#{'a': '1', 'b': '2', 'c': '3', '1': 'a', '2': 'b', '3': 'c'}

However, you should be careful of to use this because there might be duplicated keys and values each others.

Park
  • 2,446
  • 1
  • 16
  • 25
1

Assuming that there will be never collosion (i.e. keys and values are disjoint sets) then you can create "mirror" dict then harness collections.ChainMap as follows

import collections
key = {'a':'1','b':'2','c':'3'}
rev_key = dict(i[::-1] for i in key.items())
final = collections.ChainMap(key,rev_key)
print(final['a'])
print(final['3'])
print(final['c'])

output

1
c
3

WARNING: this solution assumes you want to use final only for reading. It does not support reflection of value you insert (e.g. final['d'] = '4' will NOT cause final['4'] to become 'd')

Daweo
  • 31,313
  • 3
  • 12
  • 25
1

Here is an example of how you might create a class to do this for you. Be aware that this is quite slow when setting new / editing existing values.

class DoubleDict:
    def __init__(self, *dicts, **extras):
        self.dict1 = {}
        for dct in dicts:
            self.dict1 |= dct
        self.dict1 |= extras
        self.dict2 = dict(zip(self.dict1.values(), self.dict1.keys()))
    def __getitem__(self, key):
        try: return self.dict1.__getitem__(key)
        except KeyError: return self.dict2.__getitem__(key)
    def __setitem__(self, key, value):
        if key in self.dict1:
            self.dict2.pop(self.dict1[key])
            self.dict1.pop(key)
        elif key in self.dict2:
            self.dict1.pop(self.dict2[key])
            self.dict2.pop(key)
        if value in self.dict1:
            self.dict2.pop(self.dict1[value])
            self.dict1.pop(value)
        elif value in self.dict2:
            self.dict1.pop(self.dict2[value])
            self.dict2.pop(value)
        self.dict1[key] = value
        self.dict2[value] = key
    def __iter__(self):
        total_dict = self.dict1 | self.dict2
        return total_dict.__iter__()
    def __repr__(self):
        return f"DoubleDict({repr(self.dict1)})"
    def __str__(self):
        return "\n".join(f"{key}  <==>  {self.dict1[key]}" for key in self.dict1)

Here is how it works:

a = DoubleDict({1: 2, 3: 4, 5: 6, 7: 8})
print(a)
# 1  <==>  2
# 3  <==>  4
# 5  <==>  6
# 7  <==>  8
a[1] = 3
# Now the 3,4 pair and the 1,2 pair no longer exist, but there is a new 3,1 pair
print(a[1], a[3])
# 3 1
print(a)
# 5  <==>  6
# 7  <==>  8
# 1  <==>  3
Lecdi
  • 2,189
  • 2
  • 6
  • 20
  • This probably comes closest to the desired behavior, but there's one problem: If our dict is {1:2}, {2:1} and we add 1:3, our new dict is ({1: 3} {2: 1, 3: 1}), which doesn't seem right. When popping a key, you should probably pop its value from the opposite dictionary. – kcsquared Feb 12 '22 at 13:42
  • Forgot about that. I have edited the post – Lecdi Feb 12 '22 at 13:53