I need to loop over a list until list is not empty according to a condition, also This is the code I wrote but is returning a 'list index out of range' error. What is the problem? Any suggestions on how to eventually improve this?
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
for i in range(len(l)):
if l[i]>0:
l.remove(l[i])
# store the index processing order
removed.append(i)
else:
continue
l = [x 1 for x in l]
CodePudding user response:
IIUC - it looks like you just want the index of the removed values and keep the values in the original list if they are less than or equal to and then 1 to the value
l = [0, 1, 0, 0, 1, 1]. # your list
keep_idx, lst = zip(*[(idx, i 1) for idx, i in enumerate(l) if i<=0])
print(list(keep_idx)) # -> [0, 2, 3]
print(list(lst)). # -> [1, 1, 1]
CodePudding user response:
Since you're removing items from list while trying to loop through it, your list length keeps going down but the range doesn't account for it. What you could do is looping through the list backwards. One solution is using
range(len(list_name)-1, -1, -1)
This is to loop through the array backwards and stop the out of range error
CodePudding user response:
@SomeoneElse is right.
However, in principle it is recommended to use for this a for
loop:
for i in l:
if i > 0:
...
CodePudding user response:
EDIT: I was wrong here, but I will keep the answer up so people can see where I went wrong. As Ignatius pointed out, range() is exclusive of the final item.
Original answer:
Lists use Zero-based indexing, so the first item is item 0.
An example list:
l = [1, 2, 3, 4, 5]
l contains 5 items, but indexes start at 0, so '1' is l[0] and '5' is l[4].
This is why you're getting an out of range exception. In your code you are looping up to len(l) which is 6, but l[6] is out of bounds. l[5] is the final item in your list.
Solution:
for i in range(len(l)-1)):
CodePudding user response:
The problem is the list
l
gets smaller after callingl.remove(value)
, but subscript 'i' still try to index the originall
.Based on the above analysis, one solution is to keep
l
unchanged in the inner loop, the other is to keep the unseeni
reduced along withl
.
# Create new lists to keep `l` unchanged in the inner loop
def method1():
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
next_l = []
[next_l.append(v) if v <= 0 else removed.append(i) for i, v in enumerate(l)]
l = [x 1 for x in next_l]
return removed
def method2():
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
num_del = 0 # record number of deletions in the inner loop
for i in range(len(l)):
if l[i-num_del]>0:
l.remove(l[i-num_del])
num_del = 1
# store the index processing order
removed.append(i)
else:
continue
l = [x 1 for x in l]
return removed
assert method1() == method2()
# output [1, 4, 5, 0, 1, 2]
But I guess you expect the result [1, 4, 5, 0, 2, 3]
, i.e., record the processing order with subscript in the original list. If so, try this:
l = [0, 1, 0, 0, 1, 1]
el = list(enumerate(l))
removed = []
bound = 0
while len(removed) != len(l):
removed.extend(list(filter(lambda iv: iv[1] > bound, el)))
el = list(filter(lambda iv: iv[1] <= bound, el))
bound -= 1
removed, _ = zip(*removed)