Is there a 'multimap' implementation in Python?

PythonDictionary

Python Problem Overview


I am new to Python, and I am familiar with implementations of Multimaps in other languages. Does Python have such a data structure built-in, or available in a commonly-used library?

To illustrate what I mean by "multimap":

a = multidict()
a[1] = 'a'
a[1] = 'b'
a[2] = 'c'

print(a[1])  # prints: ['a', 'b']
print(a[2])  # prints: ['c']

Python Solutions


Solution 1 - Python

Such a thing is not present in the standard library. You can use a defaultdict though:

>>> from collections import defaultdict
>>> md = defaultdict(list)
>>> md[1].append('a')
>>> md[1].append('b')
>>> md[2].append('c')
>>> md[1]
['a', 'b']
>>> md[2]
['c']

(Instead of list you may want to use set, in which case you'd call .add instead of .append.)


As an aside: look at these two lines you wrote:

a[1] = 'a'
a[1] = 'b'

This seems to indicate that you want the expression a[1] to be equal to two distinct values. This is not possible with dictionaries because their keys are unique and each of them is associated with a single value. What you can do, however, is extract all values inside the list associated with a given key, one by one. You can use iter followed by successive calls to next for that. Or you can just use two loops:

>>> for k, v in md.items():
...     for w in v:
...         print("md[%d] = '%s'" % (k, w))
... 
md[1] = 'a'
md[1] = 'b'
md[2] = 'c'

Solution 2 - Python

Just for future visitors. Currently there is a python implementation of Multimap. It's available via pypi

Solution 3 - Python

Stephan202 has the right answer, use defaultdict. But if you want something with the interface of C++ STL multimap and much worse performance, you can do this:

multimap = []
multimap.append( (3,'a') )
multimap.append( (2,'x') )
multimap.append( (3,'b') )
multimap.sort()

Now when you iterate through multimap, you'll get pairs like you would in a std::multimap. Unfortunately, that means your loop code will start to look as ugly as C++.

def multimap_iter(multimap,minkey,maxkey=None):
  maxkey = minkey if (maxkey is None) else maxkey
  for k,v in multimap:
    if k<minkey: continue
    if k>maxkey: break
    yield k,v

# this will print 'a','b'
for k,v in multimap_iter(multimap,3,3):
  print v

In summary, defaultdict is really cool and leverages the power of python and you should use it.

Solution 4 - Python

You can take list of tuples and than can sort them as if it was a multimap.

listAsMultimap=[]

Let's append some elements (tuples):

listAsMultimap.append((1,'a'))
listAsMultimap.append((2,'c'))
listAsMultimap.append((3,'d'))
listAsMultimap.append((2,'b'))
listAsMultimap.append((5,'e'))
listAsMultimap.append((4,'d'))

Now sort it.

listAsMultimap=sorted(listAsMultimap)

After printing it you will get:

[(1, 'a'), (2, 'b'), (2, 'c'), (3, 'd'), (4, 'd'), (5, 'e')]

That means it is working as a Multimap!

Please note that like multimap here values are also sorted in ascending order if the keys are the same (for key=2, 'b' comes before 'c' although we didn't append them in this order.)

If you want to get them in descending order just change the sorted() function like this:

listAsMultimap=sorted(listAsMultimap,reverse=True)

And after you will get output like this:

[(5, 'e'), (4, 'd'), (3, 'd'), (2, 'c'), (2, 'b'), (1, 'a')]

Similarly here values are in descending order if the keys are the same.

Solution 5 - Python

The standard way to write this in Python is with a dict whose elements are each a list or set. As stephan202 says, you can somewhat automate this with a defaultdict, but you don't have to.

In other words I would translate your code to

a = dict()
a[1] = ['a', 'b']
a[2] = ['c']

print(a[1])  # prints: ['a', 'b']
print(a[2])  # prints: ['c']

Solution 6 - Python

Or subclass dict:

class Multimap(dict):
    def __setitem__(self, key, value):
        if key not in self:
            dict.__setitem__(self, key, [value])  # call super method to avoid recursion
        else
            self[key].append(value)

Solution 7 - Python

There is no multi-map in the Python standard libs currently.

WebOb has a MultiDict class used to represent HTML form values, and it is used by a few Python Web frameworks, so the implementation is battle tested.

Werkzeug also has a MultiDict class, and for the same reason.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJames BondView Question on Stackoverflow
Solution 1 - PythonStephan202View Answer on Stackoverflow
Solution 2 - PythonMichalView Answer on Stackoverflow
Solution 3 - PythonamwinterView Answer on Stackoverflow
Solution 4 - Pythonhafiz031View Answer on Stackoverflow
Solution 5 - PythonpoolieView Answer on Stackoverflow
Solution 6 - PythonjayView Answer on Stackoverflow
Solution 7 - PythonLuciano RamalhoView Answer on Stackoverflow