I've got a list of tuples extracted from a table in a DB which looks like (key , foreignkey , value). There is a many to one relationship between the key and foreignkeys and I'd like to convert it into a dict indexed by the foreignkey containing the sum of all values with that foreignkey, i.e. { foreignkey , sumof( value ) }. I wrote something that's rather verbose:
myDict = {}
for item in myTupleList:
if item[1] in myDict:
myDict [ item[1] ] += item[2]
else:
myDict [ item[1] ] = item[2]
but after seeing this question's answer or these two there's got to be a more concise way of expressing what I'd like to do. And if this is a repeat, I missed it and will remove the question if you can provide the link.
Here's my (tongue in cheek) answer:
It is ugly and bad, but here is how it works.
The first argument to reduce (because it isn't clear there) is
lambda d, t: (d.__setitem__(t[1], d.get(t[1], 0) + t[2]), d)[1]
. I will talk about this later, but for now, I'll just call itjoe
(no offense to any people named Joe intended). The reduce function basically works like this:And that's for a three element list. As you can see, it basically uses its first argument to sort of accumulate each result into the final answer. In this case, the final answer is the dictionary you wanted.
Now for
joe
itself. Here isjoe
as adef
:Unfortunately, no form of
=
orreturn
is allowed in a Pythonlambda
so that has to be gotten around. I get around the lack of=
by calling thedict
s__setitem__
function directly. I get around the lack of return in by creating a tuple with the return value of__setitem__
and the dictionary and then return the tuple element containing the dictionary. I will slowly alterjoe
so you can see how I accomplished this.First, remove the
=
:Next, make the entire expression evaluate to the value we want to return:
I have run across this use-case for
reduce
anddict
many times in my Python programming. In my opinion,dict
could use a member functionreduceto(keyfunc, reduce_func, iterable, default_val=None)
.keyfunc
would take the current value from the iterable and return the key.reduce_func
would take the existing value in the dictionary and the value from the iterable and return the new value for the dictionary.default_val
would be what was passed intoreduce_func
if the dictionary was missing a key. The return value should be the dictionary itself so you could do things like:Look at SQLAlchemy and see if that does all the mapping you need and perhaps more
Assuming all your values are
int
s, you could use adefaultdict
to make this easier:defaultdict
is like a dictionary, except if you try to get a key that isn't there it fills in the value returned by the callable - in this case,int
, which returns 0 when called with no arguments.UPDATE: Thanks to @gnibbler for reminding me, but tuples can be unpacked in a for loop:
Here, the 3-item tuple gets unpacked into the variables
_
,key
, andval
._
is a common placeholder name in Python, used to indicate that the value isn't really important. Using this, we can avoid the hairyitem[1]
anditem[2]
indexing. We can't rely on this if the tuples inmyTupleList
aren't all the same size, but I bet they are.(We also avoid the situation of someone looking at the code and thinking it's broken because the writer thought arrays were 1-indexed, which is what I thought when I first read the code. I wasn't alleviated of this until I read the question. In the above loop, however, it's obvious that
myTupleList
is a tuple of three elements, and we just don't need the first one.)Maybe not exactly readable but it should work:
The first line finds all unique foreign keys. The second line builds your dictionary by first constructing a list of (fk, sum(all values for this fk))-pairs and turning that into a dictionary.