-1

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]
  • Related: https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating – jarmod Oct 03 '22 at 14:43
  • The answer by @ILS appears to be the best one. The short answer is you can't delete elements from a list as you are doing and expect the indices produced by `range(len(l))` to still be valid. Which is why you're getting a 'list index out of range' error. I'm honestly a bit perplexed why a) you expected that to work, and b) didn't understand what the error was telling you. Also, your example code and question suggests to me you are asking for help with homework. Asking S.O. to do your homework is frowned upon. – Kurtis Rader Oct 04 '22 at 04:42

4 Answers4

2

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]
It_is_Chris
  • 13,504
  • 2
  • 23
  • 41
2
  1. The problem is the list l gets smaller after calling l.remove(value), but subscript 'i' still try to index the original l.

  2. Based on the above analysis, one solution is to keep l unchanged in the inner loop, the other is to keep the unseen i reduced along with l.

# 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)
ILS
  • 1,224
  • 1
  • 8
  • 14
1

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

Tapu
  • 51
  • 6
-1

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)):
Someone Else
  • 88
  • 10
  • Thank you, still returning the same error anyhow! – Luca Giovanni Voglino Oct 03 '22 at 14:55
  • After some testing, I believe I am actually wrong about this. I think @It_is_Chris has a better answer. – Someone Else Oct 03 '22 at 14:56
  • 1
    `range()` is exclusive of the last value. So, `for i in range(len(l)-1):` will generate a range from 0 to 3, not from 0 to 4. `for i in range(len(l)):` is indeed the proper way to iterate in a list, and in fact the reason because `range` was designed that way. – Ignatius Reilly Oct 03 '22 at 14:56