views:

806

answers:

5

In C++ often do something like this:

typedef map<int, vector<int> > MyIndexType;

Where I then use it like this:

MyIndexType myIndex;
for( ... some loop ...)
{
  myIndex[someId].push_back(someVal);
}

If there was no entry in the map the code will insert a new empty vector and then append to it.

In Python it would look like this:

myIndex = {}

for (someId,someVal) in collection:
   try:
      myIndex[someId].append(someVal)
   except KeyError:
      myIndex[someId] = [someVal]

The try except is a bit ugly here. Is there a way to tell the dictionary an object type to insert when a KeyError is encountered at dictionary declaration time?

+10  A: 

Something like this perhaps:

myIndex = {}
for (someId,someVal) in collection:
    myIndex.setdefault(someId, []).append(someVal)
Alastair
This is fundamentally a better way of doing things than than ddaa's response.
Jerub
@Jerub: I disagree. Using a defaultdict is much easier to read, and has the advantage that it doesn't create and immediately destroy a new empty list even when not adding a new key (though in practice its slightly slower for lists - can be important for some types though).
Brian
I agree. setdefault() is the way to go.
Jeremy Cantrell
+15  A: 

You want to use:

from collections import defaultdict
myIndex = defaultdict(list)
myIndex[someId].append(someVal)

Standard Library defaultdict objects.

Example usage from the Python documentation:

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
        d[k].append(v)

>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
ddaa
A: 

How about this? It may not be performance-optimal, but I think it's the "simplest thing that could possibly work".

myIndex = {}

for (someId,someVal) in collection:
   if someId not in myIndex:
       myIndex[someId] = []
   myIndex[someId].append(someVal)
Matt Campbell
You are actually doing 2 lookups here. One more than needed.
James Dean
This is called "look before you leap" (LBYL). More pythonic is EAFP (google it :-) ). In this case: try: myIndex[someID].append(someVal) except KeyError: myIndex[someID] = [someVal]Of course, these days we have defaultdict which is even better :-)
John Fouhy
the last line of the example should read myIndex[someId].append(someVal)Note the lowercase 'a' in 'append'
bgbg
+2  A: 

Just to complete the answer by Alastair: There is also the get equivalent of setdefault, which is called get (and not getdefault, as one might think):

myIndex = {}
someId = None
myList = myIndex.get(someId, []) # myList is [] now
bgbg
+1  A: 

From Python 2.5 and on you can get the behavior of setdefault or using defaultdict by implementing

__missing__(k)

as in note 10 here.

Henrik Gustafsson