Is there a 'multimap' implementation in Python? - python

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']

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'

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

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.

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.

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']

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)

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.

Related

Replacing a single element in a tuple nested within a list - Is their a better way?

Edit - I want to change the value of a tuple nested in a list, at a specific position
eg changed nestedTuple[1][1] change to 'xXXXXx'
I have come up with this code, that works, but it just seems very 'Un-pure!'
Convert to a list - change - convert to tuple - insert back into list
I ASSuME that it would be very demanding on resources.
Could anyone please advise me if their is a better way?
>>> nestedTuple= [('a','b','c'), ('d','e','f'), ('g','h','i')]
>>> tempList = list(nestedTuple[1])
>>> tempList[1] = 'xXXXXx'
>>> nestedTuple[1] = tuple(tempList)
>>> print nestedTuple
[('a', 'b', 'c'), ('d', 'xXXXXx', 'f'), ('g', 'h', 'i')]
You can use slicing.
>>> i = 1
>>> nestedTuple = [('a','b','c'), ('d','e','f'), ('g','h','i')]
>>> nestedTuple[1] = nestedTuple[1][:i] + ('xXXXXx', ) + nestedTuple[1][i+1:]
>>> nestedTuple
[('a', 'b', 'c'), ('d', 'xXXXXx', 'f'), ('g', 'h', 'i')]
How about this?
nested_tuple[1] = tuple('XXXXX' if i==1 else x for i, x in enumerate(nested_tuple[1]))
Note that tuples aren't meant to be changed, so one liners aren't going to be very clean.
Depending on how many changes you want to make in your nestedTuple and depending on downstream in your program. You may want to built a nestedList from your nestedTuple
nestedList = [list(myTuple) for myTuple in nestedTuple]
and then do:
nestedList[x][y] = 'truc'
and then make a new nestedTuple if needed
Otherwise you should profile this
I understand that this is data structure that you are getting. Performance aside it would make for much cleaner and readable code to change the data to nested list, do the manipulation, and if you need to write it back to convert it back to nested tuple. May be suboptimal in terms of speed, but that might not be the limiting factor for your application.
nestedTuple= [('a','b','c'), ('d','e','f'), ('g','h','i')]
nestedList = [list(x) for x in nestedTuple]
now you can use normal list slicing and assigning
nestedList[1][1] = ['xxxxXXxxx']
if you need the data back in original nested tuple format use the one liner:
nestedTuple = [tuple(x) for x in nestedList]
most readable and least likely to contain bugs if your data structure grows and your slicing becomes more complex.
Very purpose to use tuples is its immutable means once the tuple is created the values cannot be changed. In your case the best way will be to use nested lists i.e., as shown below
>>> nestedList = [['a','b','c'], ['d','e','f'], ['g','h','i']]
Now to change the element 'e' in the list to 'xxxx' you can use as shown below
>>> nestedList[1][1] = 'xxxx'

Make a copy of a set and exclude one item

Im trying to make a set based in another set, and exclude only one item...
(do a for loop inside another for loop with an object that is inside a set, but not iterate with itself on the second loop)
Code:
for animal in self.animals:
self.exclude_animal = set((animal,))
self.new_animals = set(self.animals)
self.new_animals.discard(self.exclude_animal) # parameters for a set needs to be a set?
for other_animal in (self.new_animals):
if animal.pos[0] == other_animal.pos[0]:
if animal.pos[1] == other_animal.pos[1]:
self.animals_to_die.add(animal)
print other_animal
print animal
self.animals_to_die.add(other_animal)
Point is, my print statement returns the object id(x), so I know that they are the same object, but they should not be, I discard it on that set new_animals set.
Any insight in why this doesn't exclude the item?
set.discard() removes one item from the set, but you pass in a whole set object.
You need to remove the element itself, not another set with the element inside:
self.new_animals.discard(animal)
Demo:
>>> someset = {1, 2, 3}
>>> someset.discard({1})
>>> someset.discard(2)
>>> someset
set([1, 3])
Note how 2 was removed, but 1 remained in the set.
It would be easier to just loop over the set difference here:
for animal in self.animals:
for other_animal in set(self.animals).difference([animal]):
if animal.pos == other_animal.pos:
self.animals_to_die.add(animal)
print other_animal
print animal
self.animals_to_die.add(other_animal)
(where I assume that .pos is a tuple of two values, you can just test for direct equality here).
You don't need to store new_animals on self all the time; using local names suffices and is not even needed here.
As you mark both animals to die, you don't need to compare A to B and also B to A (which your current code does). You can ensure you get only unique pairs of animals by using itertools.combinations():
for animal, other_animal in itertools.combinations(self.animals, 2):
if animal.pos==other_animal.pos:
self.animals_to_die.update([animal, other_animal])
Just for fun, I'll point out you can even do it as a single expression (though I think it reads better as an explicit loop):
self.animals_to_die.update(sum(((animal,other_animal)
for animal,other_animal in itertools.combinations(self.animals, 2)
if animal.pos == other_animal.pos), ()))
For clarity, itertools.combinations() gives you all the unique combinations of its input. The second argument controls how many elements are selected each time:
>>> list(itertools.combinations(["A", "B", "C", "D"], 2))
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
>>> list(itertools.combinations(["A", "B", "C", "D"], 3))
[('A', 'B', 'C'), ('A', 'B', 'D'), ('A', 'C', 'D'), ('B', 'C', 'D')]
That works well in this case as the code given appears to be symmetric (two animals on the same location mutually annihilate each other). If it had been asymmetric (one kills the other) then you would have to remember to test both ways round on each iteration.

Generate combinations of elements from multiple lists

I'm making a function that takes a variable number of lists as input (i.e., an arbitrary argument list).
I need to compare each element from each list to each element of all other lists, but I couldn't find any way to approach this.
Depending on your goal, you can make use of some of the itertools utilities. For example, you can use itertools.product on *args:
from itertools import product
for comb in product(*args):
if len(set(comb)) < len(comb):
# there are equal values....
But currently it's not very clear from your question what you want to achieve. If I didn't understand you correctly, you can try to state the question in a more specific way.
I think #LevLeitsky's answer is the best way to do a loop over the items from your variable number of lists. However, if purpose the loop is just to find common elements between pairs of items from the lists, I'd do it a bit differently.
Here's an approach that finds the common elements between each pair of lists:
import itertools
def func(*args):
sets = [set(l) for l in args]
for a, b in itertools.combinations(sets, 2):
common = a & b # set intersection
# do stuff with the set of common elements...
I'm not sure what you need to do with the common elements, so I'll leave it there.
The itertools module provides a lot of useful tools just for such tasks. You can adapt the following example to your task by integrating it into your specific comparison logic.
Note that the following assumes a commutative function. That is, about half of the tuples are omitted for reasons of symmetry.
Example:
import itertools
def generate_pairs(*args):
# assuming function is commutative
for i, l in enumerate(args, 1):
for x, y in itertools.product(l, itertools.chain(*args[i:])):
yield (x, y)
# you can use lists instead of strings as well
for x, y in generate_pairs("ab", "cd", "ef"):
print (x, y)
# e.g., apply your comparison logic
print any(x == y for x, y in generate_pairs("ab", "cd", "ef"))
print all(x != y for x, y in generate_pairs("ab", "cd", "ef"))
Output:
$ python test.py
('a', 'c')
('a', 'd')
('a', 'e')
('a', 'f')
('b', 'c')
('b', 'd')
('b', 'e')
('b', 'f')
('c', 'e')
('c', 'f')
('d', 'e')
('d', 'f')
False
True
if you want the arguments as dictionary
def kw(**kwargs):
for key, value in kwargs.items():
print key, value
if you want all the arguments as list:
def arg(*args):
for item in args:
print item
you can use both
def using_both(*args, **kwargs) :
kw(kwargs)
arg(args)
call it like that:
using_both([1,2,3,4,5],a=32,b=55)

Python semantics - Multiple parameters in declaration of for loop

Sorry to ask a dumb question, but could somebody tell me what the following means
for ctype, (codename, name) in searched_perms:
I don't understand what's going on the parenthesis. for ctype in serached_perms: would make sense.
I realise that the answer is in the python docs somewhere but since I don't know what I'm looking for, it's difficult to find the explaination.
This is practically equivalent to
for x in search_perms:
ctype, y = x
codename, name = y
or
for x in search_perms:
ctype = x[0]
codename = x[1][0]
name = x[1][1]
i.e., it unpacks the items from search_perms as pairs, then unpacks the second item of each pair as a pair as well.
Example:
>>> d = {"ham": "yes", "spam": "no", "eggs": "please"}
>>> for k, v in d.iteritems():
... print("%s? %s" % (k, v))
...
eggs? please
ham? yes
spam? no
>>> for i, (k, v) in enumerate(d.iteritems()):
... print("question %d: %s? %s" % (i, k, v))
...
question 0: eggs? please
question 1: ham? yes
question 2: spam? no
This works because enumerate(d.iteritems()) generates pairs where each second element is again a pair:
>>> list(enumerate(d.iteritems()))
[(0, ('eggs', 'please')), (1, ('ham', 'yes')), (2, ('spam', 'no'))]
ctype, (codename, name) is the same thing as (ctype, (codename, name)). Therefore, searched_perms needs to be a sequence of things of the form (a,(b,c)), and on each loop iteration the variables ctype, codename, name will be set to a,b,c.
Your list searchedparams should looks something like this:
In [1]: L = []
In [2]: L.append(('a', ('b', 'c')))
In [3]: L.append(('d', ('e', 'f')))
In [4]: L
Out[4]: [('a', ('b', 'c')), ('d', ('e', 'f'))]
In [6]: for ctype, (codename, name) in L:
print ctype, codename, name
...:
a b c
d e f
('a', ('b', 'c')) is a tuple of 2 values, where the right value is also a tuple of two values.
It means that searched_perms is an iterable that returns two elements during iteration: the first one is ctype and the second is a tuple, composed of another two elements: (codename, name). So searched_perms looks something like this:
[[ctype1, (code1, name1)], [ctype2, (code2, name2)], ...]
The syntax for ctype, (codename, name) in searched_perms allows the extraction of all the contents in searched_perms, element by element.

Sorting a tuple that contains tuples [duplicate]

This question already has answers here:
How to sort a list of objects based on an attribute of the objects in descending order?
(9 answers)
Closed 28 days ago.
I have the following tuple, which contains tuples:
MY_TUPLE = (
('A','Apple'),
('C','Carrot'),
('B','Banana'),
)
I'd like to sort this tuple based upon the second value contained in inner-tuples (i.e., sort Apple, Carrot, Banana rather than A, B, C).
Any thoughts?
from operator import itemgetter
MY_SORTED_TUPLE = tuple(sorted(MY_TUPLE, key=itemgetter(1)))
or without itemgetter:
MY_SORTED_TUPLE = tuple(sorted(MY_TUPLE, key=lambda item: item[1]))
From Sorting Mini-HOW TO
Often there's a built-in that will
match your needs, such as str.lower().
The operator module contains a number
of functions useful for this purpose.
For example, you can sort tuples based
on their second element using
operator.itemgetter():
>>> import operator
>>> L = [('c', 2), ('d', 1), ('a', 4), ('b', 3)]
>>> map(operator.itemgetter(0), L)
['c', 'd', 'a', 'b']
>>> map(operator.itemgetter(1), L)
[2, 1, 4, 3]
>>> sorted(L, key=operator.itemgetter(1))
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]
Hope this helps.
sorted(my_tuple, key=lambda tup: tup[1])
In other words, when comparing two elements of the tuple you're sorting, sort based on the return value of the function passed as the key parameter.
I achieved the same thing using this code, but your suggestion is great. Thanks!
templist = [ (line[1], line) for line in MY_TUPLE ]
templist.sort()
SORTED_MY_TUPLE = [ line[1] for line in templist ]

Categories

Resources