[python] python : list index out of range error while iteratively popping elements

I have written a simple python program

l=[1,2,3,0,0,1]
for i in range(0,len(l)):
       if l[i]==0:
           l.pop(i)

This gives me error 'list index out of range' on line if l[i]==0:

After debugging I could figure out that i is getting incremented and list is getting reduced.
However, I have loop termination condition i < len(l). Then why I am getting such error?

This question is related to python list

The answer is


You are reducing the length of your list l as you iterate over it, so as you approach the end of your indices in the range statement, some of those indices are no longer valid.

It looks like what you want to do is:

l = [x for x in l if x != 0]

which will return a copy of l without any of the elements that were zero (that operation is called a list comprehension, by the way). You could even shorten that last part to just if x, since non-zero numbers evaluate to True.

There is no such thing as a loop termination condition of i < len(l), in the way you've written the code, because len(l) is precalculated before the loop, not re-evaluated on each iteration. You could write it in such a way, however:

i = 0
while i < len(l):
   if l[i] == 0:
       l.pop(i)
   else:
       i += 1

x=[]
x = [int(i) for i in input().split()]
i = 0
    while i < len(x):
        print(x[i])
        if(x[i]%5)==0:
            del x[i]
        else:
            i += 1
print(*x)

I think most solutions talk here about List Comprehension, but if you'd like to perform in place deletion and keep the space complexity to O(1); The solution is:

i = 0
for j in range(len(arr)):
if (arr[j] != 0):
    arr[i] = arr[j]
    i +=1
arr = arr[:i] 

You're changing the size of the list while iterating over it, which is probably not what you want and is the cause of your error.

Edit: As others have answered and commented, list comprehensions are better as a first choice and especially so in response to this question. I offered this as an alternative for that reason, and while not the best answer, it still solves the problem.

So on that note, you could also use filter, which allows you to call a function to evaluate the items in the list you don't want.

Example:

>>> l = [1,2,3,0,0,1]
>>> filter(lambda x: x > 0, l)
[1, 2, 3]

Live and learn. Simple is better, except when you need things to be complex.


The expression len(l) is evaluated only one time, at the moment the range() builtin is evaluated. The range object constructed at that time does not change; it can't possibly know anything about the object l.

P.S. l is a lousy name for a value! It looks like the numeral 1, or the capital letter I.


Code:

while True:
        n += 1
        try:
          DATA[n]['message']['text']
        except:
          key = DATA[n-1]['message']['text']
          break

Console :

Traceback (most recent call last):
  File "botnet.py", line 82, in <module>
    key =DATA[n-1]['message']['text']
IndexError: list index out of range

The problem was that you attempted to modify the list you were referencing within the loop that used the list len(). When you remove the item from the list, then the new len() is calculated on the next loop.

For example, after the first run, when you removed (i) using l.pop(i), that happened successfully but on the next loop the length of the list has changed so all index numbers have been shifted. To a certain point the loop attempts to run over a shorted list throwing the error.

Doing this outside the loop works, however it would be better to build and new list by first declaring and empty list before the loop, and later within the loop append everything you want to keep to the new list.

For those of you who may have come to the same problem.


What Mark Rushakoff said is true, but if you iterate in the opposite direction, it is possible to remove elements from the list in the for-loop as well. E.g.,

x = [1,2,3,0,0,1]
for i in range(len(x)-1, -1, -1):
    if x[i] == 0:
        x.pop(i)

It's like a tall building that falls from top to bottom: even if it is in the middle of collapse, you still can "enter" into it and visit yet-to-be-collapsed floors.


I think the best way to solve this problem is:

l = [1, 2, 3, 0, 0, 1]
while 0 in l:
    l.remove(0)

Instead of iterating over list I remove 0 until there aren't any 0 in list


I recently had a similar problem and I found that I need to decrease the list index by one.

So instead of:

if l[i]==0:

You can try:

if l[i-1]==0:

Because the list indices start at 0 and your range will go just one above that.


I am using python 3.3.5. The above solution of using while loop did not work for me. Even if i put print (i) after len(l) it gave me an error. I ran the same code in command line (shell)[ window that pops up when we run a function] it runs without error. What i did was calculated len(l) outside the function in main program and passed the length as a parameter. It worked. Python is weird sometimes.


List comprehension will lead you to a solution.

But the right way to copy a object in python is using python module copy - Shallow and deep copy operations.

l=[1,2,3,0,0,1]
for i in range(0,len(l)):
   if l[i]==0:
       l.pop(i)

If instead of this,

import copy
l=[1,2,3,0,0,1]
duplicate_l = copy.copy(l)
for i in range(0,len(l)):
   if l[i]==0:
       m.remove(i)
l = m

Then, your own code would have worked. But for optimization, list comprehension is a good solution.