Unexpected IndexError while removing list items [duplicate]

You are changing the length of the list while looping over a range that goes up to the starting length of the list; remove one item from the list and the last index is no longer valid.

Moveover, because items are removed from the list at the current index, the rest of the list indices shift; what was at index i + 1 is now at index i and your loop index is no longer useful.

Last but not least, you are looping until the very last index of test, but then try to access test[i + 1] still; that index does not exist even if you were not removing elements from the list.

You could use a while loop to achieve what you want to do:

test = ['aac', 'aad', 'aac', 'asd', 'msc']
i = 0
while i < len(test) - 1:
    if test[i][:2] == test[i+1][:2]:
        del test[i]
        continue
    i += 1

Now i is tested against the new length each loop iteration, and we only increment i if no element was removed. Note that the loop is limited to the length minus 1 because you want to test for test[i + 1] each iteration.

Note that I use del test[i]; no need to scan through the list searching for that the value-to-remove again; this could lead to subtle bugs as well if values appear multiple times in the list but only later instances should be removed; e.g. ['aac', 'foo', 'aac', 'aad'] should result in ['aac', 'foo', 'aad'], not ['foo', 'aac', 'aad'], which is what test.remove(test[i]) would result in.

Demo:

>>> test = ['aac', 'aad', 'aac', 'asd', 'msc']
>>> i = 0
>>> while i < len(test) - 1:
...     if test[i][:2] == test[i+1][:2]:
...         del test[i]
...         continue
...     i += 1
... 
>>> test
['aac', 'asd', 'msc']

You could use a list comprehension to avoid the shrinking list problem:

>>> [t for i, t in enumerate(test) if i == len(test) - 1 or t[:2] != test[i + 1][:2]]
['aac', 'asd', 'msc']

Both approaches require only one loop through the input list.

Leave a Comment