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.