Home > Enterprise >  How to conditionally sum elements of a list in python?
How to conditionally sum elements of a list in python?

Time:09-22

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.

CodePudding user response:

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.

CodePudding user response:

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.

CodePudding user response:

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

CodePudding user response:

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