M. Paczek
M. Paczek

Reputation: 67

How to sum up a list of tuples having the same first element?

I have a list of tuples, for example:

    (1,3)
    (1,2)
    (1,7)
    (2,4)
    (2,10)
    (3,8)

I need to be able to sum up the second values based upon what the first value is, getting this result for the example list:

    (1,12)
    (2,14)
    (3,8)

This question is very similar in nature to this one, however, my solution may not use any imports or for loops, and all answers to that question use one or the other. It's supposed to rely on list and set comprehension.

Upvotes: 3

Views: 3674

Answers (5)

576i
576i

Reputation: 8362

I'd use a defaultdict

from collections import defaultdict

x = [(1,3), (1, 2), (1, 7), (2, 4), (2, 10), (3, 8)]

d = defaultdict(int)

for k, v in x:
    d[k] += v

print(list(d.items()))

if you need a one-liner (lambda inline function) using itertools

from itertools import groupby

myfunc = lambda tu : [(k, sum(v2[1] for v2 in v)) for k, v in groupby(tu, lambda x: x[0])])

print(myfunc(x))

Upvotes: 0

aghast
aghast

Reputation: 15310

This is pretty straightforward, but it's definitely O(n**2), so keep your input data small:

data = (
    (1,3),
    (1,2),
    (1,7),
    (2,4),
    (2,10),
    (3,8),
)

d = { k:v for k,v in data }
d2 = [(t1,sum( v for k,v in data if k == t1 )) for t1 in d.keys() ]
print(d2)

Output is

[(1, 12), (2, 14), (3, 8)]

Upvotes: 0

Padraic Cunningham
Padraic Cunningham

Reputation: 180411

If you are using python2, you can use map to behave like izip_longest and get the index of where the groups end:

def sums(l):
    st = set()
    inds = [st.add(a) or ind for ind, (a, b) in enumerate(l) if a not in st]
    return [(l[i][0], sum(sub[1] for sub in l[i:j])) for i, j in map(None, inds, inds[1:])]

Output:

In [10]: print(sums(l))
[(1, 12), (2, 14), (3, 8)]

for python 2 or 3 you can just use enumerate and check the index:

def sums(l):
    st = set()
    inds = [st.add(a) or ind for ind, (a, b) in enumerate(l) if a not in st]
    return [(l[j][0], sum(sub[1] for sub in (l[j:inds[i]] if i < len(inds) else l[inds[-1]:])))
            for i, j in enumerate(inds, 1)]

same output:

In [12]: print(sums(l))
[(1, 12), (2, 14), (3, 8)]

Upvotes: 0

Tim Martin
Tim Martin

Reputation: 2509

If you can use dictionaries the following should work

x = [(1,3), (1, 2), (1, 7), (2, 4), (2, 10), (3, 8)]
d = {}
[d.__setitem__(first, d.get(first, 0) + second) for first, second in x]
print(list(d.items()))

Upvotes: 0

Joran Beasley
Joran Beasley

Reputation: 113978

my_set = {x[0] for x in my_tuples}
my_sums = [(i,sum(x[1] for x in my_tuples if x[0] == i)) for i in my_set]

I guess. ... those requirements are not very good for this problem (this solution will be slow ...)

Upvotes: 3

Related Questions