How to conditionally sum elements of a list in python?

Viewed 63

I have

a bunch of lists of integers from 0 to somewhere around 1000, here are the first elements of such a list:

oldlist = [216, 216, 199, 253, 271, 217, 183, 225, 199, 217, 217, 235, 254, 217, 235, 235, 234, 234, 235, 231, 183, 263, 298, 190, 248, 200, 223, 199, 225, 195, 240]

I want

to add consecutive list elements that are >215 and merge them into a single list element, leaving the rest of the list as it is. For the above list it should result in:

newlist = [432, 199, 741, 183, 225, 199, 2544, 183, 561, 190, 248, 200, 223, 199, 225, 195, 240]

I tried

def dtadder(oldlist):
    newlist = []
    nlidx = 0  # newlist index
    for idx, x in enumerate(oldlist):
        if idx > 0 and oldlist[idx - 1] > 215 and oldlist[idx] > 215:
            x = oldlist[idx] + newlist[nlidx - 1]
            newlist.remove(newlist[nlidx - 1])
            nlidx -= 1
        newlist.append(x)
        nlidx += 1
    return newlist

What happened

is that everything works out exactly how I expect it to, until the 116th iteration of the loop (nlidx=85), when for some reason newlist[4]= 225 is removed from the list. This keeps occasionally happening with other elements though I haven't figured out when and why. It seems that only elements >215 are removed.

What am I missing here? I'm fairly new to programming and python but I feel there should be an easier and more readable way to do this. Apart from a solution to my problem I would be really interested in understanding why my solution doesn't work.

4 Answers

You can try itertools.groupby:

from itertools import groupby

out = []
for v, g in groupby(oldlist, lambda x: x > 215):
    if v:
        out.append(sum(g))
    else:
        out.extend(g)

print(out)

Note: your code doesnt work, because list.remove removes first occurence of the value. This is probably not what you want.

You could also iterate through the list like you have done but without having to worry about the index of every item:

oldlist = [216, 216, 199, 253, 271, 217, 183, 225, 199, 217, 217, 235, 254, 217, 235, 235, 234, 234, 235, 231, 183, 263, 298, 190, 248, 200, 223, 199, 225, 195, 240]

def get_nums_more_than_250():
    temp_nums_to_add = 0
    new_list = []
    for i in oldlist:
        if i >215:
            temp_nums_to_add += i #add numbers togther 
        else:
            if temp_nums_to_add !=0:
                new_list.append(temp_nums_to_add)
                temp_nums_to_add = 0
            new_list.append(i)
    
    #for final iteration (if values are stored in temp_nums_to_add
    if temp_nums_to_add !=0:
        new_list.append(temp_nums_to_add)
    return new_list
    
print(get_nums_more_than_250())

Although this isn't as sleek as the other solutions I thought I should still show mine.

You could go through all numbers, and your reaction to the new number depends on the last number you had. So you will have two states: (last number was small) and (last number was big), and depending on the state, you react to the new number accordingly.

class State:
    SMALLER = 101
    BIGGER = 102
def dtadder(old):
    new = []
    state = State.SMALLER
    tmp_sum = 0
    
    for number in old:
        match state:
            case State.SMALLER:
                if number <= 215:
                    new.append(number)
                else:
                    tmp_sum = number
                    state = State.BIGGER
            case State.BIGGER:
                if number <= 215:
                    new.append(tmp_sum)
                    tmp_sum = 0
                    new.append(number)
                    state = State.SMALLER
                else:
                    tmp_sum += number
    return new

A variant on what Andrej came up with that allows it to reduce to a listcomp (by removing the conditional choice between append and extend):

from itertools import groupby

newlist = [sum(grp) for _, grp in groupby(oldlist, key=lambda x: x > 215 or object())]

Basically, the key function returns either True (for values above 215, which all group together) or a guaranteed unique object that will not be equal to any other object (for values <= 215) so they always group as a group of just one object. Thus, you can unconditionally sum all groups, rather than treating different types of groups differently.

Related