Search code examples
pythondictionaryswapin-place

In-place Python dictionary inverse


The task that I wanted to see if possible to solve is, swapping key,value pairs of a dictionary (in Python), with an in-place calculation, without additional data-structures (Only a constant number of extra variables). It seems rather impossible (in a finite world), but I'm open to hear suggestions on solving it.

I've seen a few posts about in-place dictionary inverse in python, and I've found one common thing between all of the solutions. The following dictionary won't be properly inversed:

dict = {'b':'a','a':'c','c':123}

The reason for that is, when swapping the first argument, we overwrite 'a''s actual value (The values are unique, the keys are unique, but that doesn't mean there isn't a value that is the same as an already existing key)

NOTES:

1) The dictionary given as an example has hashable values.

2) The key/values can be of any data-type. Not necessarily strings.

I'd love to hear ways to solve it, I've thought of one but it only works if we have infinite memory, which obviously is not true.

EDIT:

1) My Idea was, changing the dictionary such that I add a constant number of underscores ("_") to the beginning of each key entry. The number of underscores is determined based on the keys, if some key has X underscores, I'll add X+1 underscores (max_underscores_of_key_in_prefix+1). To work around objects in the keys, I'll make a wrapper class for that. I have tried my best explaining my intuition, but I am not sure this is practical.

2) @Mark Ransom's solution works perfectly, but if anyone has an other algorithmic solution to the problem, I'd still love to hear it out! I mark this question as solved because it is solved, but again, other solutions are more than welcome :-)


Solution

  • Obviously for this to be possible, both keys and values must be hashable. This means that none of your keys or values can be a list. We can take advantage of this to know which dictionary elements have been already processed.

    Since you can't iterate and modify a dictionary at the same time, we must start over every time we swap a key/value. That makes this very slow, an O(n^2) operation.

    def invert_dict(d):
        done = False
        while not done:
            done = True
            for key, val in d.items():
                if isinstance(val, list):
                    if len(val) > 1:
                        d[key] = [val[1]]
                        val = val[0]
                else:
                    del d[key]
                if not isinstance(val, list):
                    if val in d:
                        d[val] = [d[val], key]
                    else:
                        d[val] = [key]
                    done = False
                    break
        for key, val in d.items():
            d[key] = val[0]