2

I have this code in Python where I'm trying to remove all instances of the number 1 from a list. I also know that:

  1. It's not possible to mutate a list while iterating over it.
  2. remove() removes the first occurrence of an item which has multiple instances of it in the list.
my_list = [2, 3, 1, 5, 8, 4, 6, 1, 1]
print(my_list)

for num in my_list:
    if num == 1:
        my_list.remove(num)

print(my_list)

The output:

[2, 3, 1, 5, 8, 4, 6, 1, 1]
[2, 3, 5, 8, 4, 6, 1]

Considering the two points I mentioned, how is it that the list changes? And more importantly, why is the last 1 not removed from the list?

Alexander O'Mara
  • 58,688
  • 18
  • 163
  • 171
Manish Giri
  • 3,562
  • 8
  • 45
  • 81
  • 1
    "It's not possible to mutate a list while iterating over it.". It is possible, but generally it's not a wise thing to do. –  Oct 18 '15 at 07:30
  • 1
    Maybe consider that you are wrong about one ofrmore of the points you mentioned and take it from there? – juanchopanza Oct 18 '15 at 07:30
  • 1
    It might be clearer to create a new list from the old list using a list comprehension, dropping all occurrences of `1`. –  Oct 18 '15 at 07:34
  • What exactly are you trying to do? You're algorithm does not strike me as efficient. – Alexander O'Mara Oct 18 '15 at 07:34
  • You should write `while 1 in my_list: my_list.remove(1)` – Gershon Papi Oct 18 '15 at 07:39
  • @AlexanderO'Mara I was just trying to see if I can get remove() to remove multiple occurrences of a number by iterating over a list. The book I'm reading says remove() only works for the first instance. – Manish Giri Oct 18 '15 at 07:39
  • What evert said. Removing items from a list you're iterating is legal, but it doesn't do what you might expect. To see why, put a 'print(num) before your `if` statement. – PM 2Ring Oct 18 '15 at 07:40
  • @GeriPapi But that would require looping over the list from the start every time to remove the next item. – Alexander O'Mara Oct 18 '15 at 07:40
  • @AlexanderO'Mara Correct, but he didn't ask for an efficient answer. Also, my Python knowledge is limited, that's why I prefered commenting than answering. – Gershon Papi Oct 18 '15 at 07:43
  • Try the suggestions from http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python `mylist = [i for i in my_list if i != 1]` – ziddarth Oct 18 '15 at 07:48
  • FWIW, `list.remove` is convenient, but it's inefficient: it has to perform a linear search over the list to find the item to remove, and then it has to move all the succeeding items down to fill the gap. True, it does those operations at C speed, but it's still best to avoid using it when there are better ways. – PM 2Ring Oct 18 '15 at 07:53

2 Answers2

1

As the commenters said, there is actually no restriction about being able to mutate the list. Take a look at this output and see if it helps.

>>> mylist = [1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> for num in mylist:
...   print(num)
...   if num == 1:
...     mylist.remove(num)
...
1
3
1
3
1
3
>>> print(mylist)
[2, 3, 2, 3, 2, 3]

Do you see how it skips the 2s? It is traversing the list from index[0] to index[-1]. When you call remove() it keeps skipping to the next index point. You never removed the 1 in your example because the for loop had already iterated over that index point.

mateor
  • 1,293
  • 1
  • 16
  • 19
0

1. You actually can modify a list while iterating it:

for i in range(len(my_list)-1, -1, -1):
    if my_list[i] == 1:
        del my_list[i]

This is a nice trick that I saw sometime ago in one of the answers here in SO (I don't remember where otherwise I would add a link). By iterating the list backwards you can delete keys and still able to continue iterating indices that are lower than the one you've just deleted!

  1. According to the docs, remove *should remove only the first occurrence. It's doing exactly what it's supposed to do!
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129