wesWWW
wesWWW

Reputation: 11

Python - List of Tuples, sum of tuples having same first value in this case?

li=[('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]

Assuming I have this case. Where it starts with the same name in the first position, I want to add the second with the second and third with the third.

Desired result:

[('name1', 11, 13), ('name2', 3, 2)]

does anyone know how I can do this in Python?

Upvotes: 1

Views: 426

Answers (7)

dawg
dawg

Reputation: 103844

Given:

LoT=[('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]

You can first use a dict to accumulate the like tags:

di={}
for t in LoT:
    di.setdefault(t[0], []).append(t[1:])

>>> di
{'name1': [(5, 10), (6, 3)], 'name2': [(3, 2)]}

Then create a new list with the sum of those tags:

>>> [(k,)+tuple(map(sum, zip(*li))) for k,li in di.items()]
[('name1', 11, 13), ('name2', 3, 2)]
# the interesting thing here is the zip(*li) which rotates the list of tuples
# zip(*[(5, 10), (6, 3)]) => [(5, 6), (10, 3)]

Or, just keep a running total:

di={}
for t in LoT:
    try:
        di[t[0]]=tuple(map(sum, zip(di[t[0]], t[1:])))
    except KeyError:
        di[t[0]]=t[1:]

And then convert the running total dict to a list:

[(k,)+t for k,t in di.items()]
# same result

These solutions work regardless of the length of the tuples.

Upvotes: 0

Vinay Emmaadii
Vinay Emmaadii

Reputation: 145

Much simpler solution:

i = [('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]
d = {}
for name, fv, sv in i:
    if name not in d:
        d[name] = [name, fv, sv]
    else:
        d[name][1] += fv
        d[name][2] += sv
        
[tuple(v) for v in d.values()]

Upvotes: 0

frankr6591
frankr6591

Reputation: 1247

Here is a method using class defintion:

class mytuple(object):
    def __init__(self, tx, val1, val2):
        self.tx = tx
        self.val1 = val1
        self.val2 = val2

    def __add__(self, t2):
        if self.tx != t2.tx : 
            print("WARNING: BAD TUPLE, ignoring", self.tx, t2.tx)

        return mytuple(self.tx, 
                       self.val1 + t2.val1,
                       self.val2 + t2.val2)

    def __str__(self):
        self.__repr()

    def __repr__(self):
        tstr = ', '.join([self.tx, 
                        '%d'%self.val1,
                        '%d'%self.val2])
        return('(%s)'%tstr)

arr = [ mytuple('name1', 5, 10),
        mytuple('name2', 3, 2), 
        mytuple('name1', 6, 3)]

print([arr[0] + arr[2], arr[1]])
[(name1, 11, 13), (name2, 3, 2)]

Upvotes: 0

Mark Moretto
Mark Moretto

Reputation: 2348

Yet another answer, but with some built in flexibility:

li=[('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]

# Get length of structure
li_len = len(li)

# Create empty collection
outdict = {}

# Iterate over all elements
for i in range(li_len):
    tmpname = li[i][0] # Temp name variable
    tmpvalues = li[i][1:] # Temp values variable

    # If not temp name in dictionary keys
    if not tmpname in outdict:
        # Create entry with values as first values
        outdict[tmpname] = list(tmpvalues)
    else:
        # Otherwise, iterate values and update entry by position
        for j in range(len(tmpvalues)):
            outdict[tmpname][j] += tmpvalues[j]

# Reformat for output
result = [tuple([k] + v) for k, v in outdict.items()]

# Print result
print(result)

Output:

[('name1', 11, 13), ('name2', 3, 2)]

Upvotes: -1

IsharM
IsharM

Reputation: 153

Initially tuple's values cant be changed but if you want to do it the way you want then you could do something like this.

li = [['name1', 5, 10], ['name2', 3, 2], ['name1', 6, 3]]
new_li = []
for tup in li:
    names = [tup[0] for tup in new_li]
    if tup[0] in names:
        index = names.index(tup[0])
        new_li[index][1] += tup[1]
        new_li[index][2] += tup[2]
    else:
        new_li.append(tup)

# [['name1', 11, 13], ['name2', 3, 2]]
# to make it tuple

new_li = [tuple(l) for l in new_li]
# [('name1', 11, 13), ('name2', 3, 2)]

Upvotes: 0

frankr6591
frankr6591

Reputation: 1247

You should define the tuple as a class. But if the data is given as a set of tuples... then define a function addTuple.

def addTuple(t1, t2):
    (ta,va1,va2) = t1

    try: (ta,va1,va2) = t1
    except: 

        # FIXME: default to None, value 0
        (ta,va1, va2) = (None, 0, 0)
    try:
        # FIXME: default to None, value 0
        (tb,vb1,vb2) = t2
    except: 
        # FIXME: default to None, value 0
        (tb,vb1, vb2) = (None, 0, 0)

    return (ta, va1+vb1, va2+vb2)

print([addTuple(arr[0],arr[2]), addTuple(arr[1],0)])
[('name1', 1

Upvotes: 0

Benedictanjw
Benedictanjw

Reputation: 838

names = [('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]

### Iterate over all members of the original list to perform the logic
new_names = dict()
for n,first,second in names:
    if n not in new_names:
        new_names[n] = (first, second)
    else:
        new_names[n] = (first+new_names[n][0], second+new_names[n][1])

### Get back list of tuples
new_names2 = [(k, *v) for k,v in new_names.items()]
print(new_names2)

Output:

[('name1', 11, 13), ('name2', 3, 2)]

Upvotes: 0

Related Questions