views:

93

answers:

4

I have a list of (label, count) tuples like this:

[('grape', 100), ('grape', 3), ('apple', 15), ('apple', 10), ('apple', 4), ('banana', 3)]

From that I want to sum all values with the same label (same labels always adjacent) and return a list in the same label order:

[('grape', 103), ('apple', 29), ('banana', 3)]

I know I could solve it with something like:

def group(l):
    result = []
    if l:
        this_label = l[0][0]
        this_count = 0
        for label, count in l:
            if label != this_label:
                result.append((this_label, this_count))
                this_label = label
                this_count = 0
            this_count += count
        result.append((this_label, this_count))
    return result

But is there a more Pythonic / elegant / efficient way to do this?

+2  A: 

using itertools and list comprehensions

import itertools

[(key, sum(num for _, num in value))
    for key, value in itertools.groupby(l, lambda x: x[0])]

Edit: as gnibbler pointed out: if l isn't already sorted replace it with sorted(l).

cobbal
to use groupby you must first ensure that the sequence is pregrouped (all the 'grape' adjacent, etc). one way to do that is to sort the sequence first
gnibbler
The OP stated the labels were already grouped.
Thomas Wouters
@Thomas Wouters, yes you are correct ("same labels are always adjacent")
gnibbler
+7  A: 

itertools.groupby can do what you want:

import itertools
import operator

L = [('grape', 100), ('grape', 3), ('apple', 15), ('apple', 10),
     ('apple', 4), ('banana', 3)]

def accumulate(l):
    it = itertools.groupby(l, operator.itemgetter(0))
    for key, subiter in it:
       yield key, sum(item[1] for item in subiter) 

>>> print list(accumulate(L))
[('grape', 103), ('apple', 29), ('banana', 3)]
>>> 
Thomas Wouters
I like the use of `operator.itemgetter` in place of `lambda`.
jathanism
+1  A: 
import collections
d=collections.defaultdict(int)
a=[]
alist=[('grape', 100), ('banana', 3), ('apple', 10), ('apple', 4), ('grape', 3), ('apple', 15)]
for fruit,number in alist:
    if not fruit in a: a.append(fruit)
    d[fruit]+=number
for f in a:
    print (f,d[f])

output

$ ./python.py
('grape', 103)
('banana', 3)
('apple', 29)
ghostdog74
+3  A: 
>>> from itertools import groupby
>>> from operator import itemgetter
>>> L=[('grape', 100), ('grape', 3), ('apple', 15), ('apple', 10), ('apple', 4), ('banana', 3)]
>>> [(x,sum(map(itemgetter(1),y))) for x,y in groupby(L, itemgetter(0))]
[('grape', 103), ('apple', 29), ('banana', 3)]
gnibbler
+1 neat! (15 chars)
S.Mark