0

I'm doing some coding exercises and cannot understand why the code I wrote removes only some odd numbers, rather than all of them (the goal is to remove ALL odd numbers and return a list of only even numbers):

def purify(numlist):
  for i in numlist:
    if i % 2 != 0:
      numlist.remove(i)
  return numlist
print (purify([1, 3, 4, 6, 7]))

I realize there are other ways to reach a solution, but my question is why the particular code above isn't working. The list argument I passed above returns [3, 4, 6] as opposed to just [4, 6]. The remainder of 3 divided by 2 is NOT 0, so why is it not also removed? Have I perhaps used the remove method in an incorrect way? Does the phrase numlist.remove(i) not overwrite numlist after removing i at each iteration?

2 Answers2

1

You are removing objects from the list you are iterating over.

def purify(numlist):
    purified = []
    for elt in numlist:
        if elt % 2 != 0:
            purified.append(elt)
    return purified

print (purify([1, 3, 4, 6, 7]))
Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
  • Thanks, but why should it make a difference, as in my version it should logically start by removing 1, making the list [3, 4, 6, 7], then remove 3, which would make the list [4, 6, 7], and so on (but of course it doesn't remove 3, to my surprise)? I now accept as fact that I should not iterate over a list that I am removing objects from, but I still don't understand why not. – Seeing what the fuss is about Nov 29 '17 at 05:28
  • As objects are removed from the list you are iterating over, you are upsetting the iteration order; some items are skipped, because the list has been altered in the loop. – Reblochon Masque Nov 29 '17 at 05:30
  • I suppose this has to do with index locations, and in order to avoid that we can either add to a brand new blank list like you have done above, or make a static copy of the original list using [:] and iterate over that if we want to do it by the remove method. Thanks for your explanation. – Seeing what the fuss is about Nov 29 '17 at 19:05
  • You are welcome, I am glad I could help, If you feel that my answer helped you, you could consider [what to do when someone answers my question](https://stackoverflow.com/help/someone-answers), and [how to accept my answer](http://meta.stackexchange.com/a/5235) – Reblochon Masque Nov 30 '17 at 01:15
1

It's not a good idea to remove elements from lists while iterating over it, which is causing issue here. You can create separate list as follows.

code:

def purify(numlist):
    res = []
    for i in numlist:
        if i % 2 == 0:
            res.append(i)
    return res
print (purify([1, 3, 4, 6, 7]))

output:

[4, 6]
Mahesh Karia
  • 2,045
  • 1
  • 12
  • 23
  • Thanks, but why is it not a good idea to remove elements from a list I am iterating over? Logically, my version should work the same way as yours - keeps removing elements that fit defined criteria and iterate over the updated list, until the list is exhausted. – Seeing what the fuss is about Nov 29 '17 at 05:31
  • When you're iterating over list & remove elements at same time then index of elements gets updated in list. Now say you have list [1, 3, 4, 6, 7] here if you remove element 1 which is at index 0 so list will be [3, 4, 6, 7] and now at index 0 element 3 is present but for loop has already visited element at index 0 and iteration will resume from index 1 skipping element 3 which is positioned to index 0 after removal of element 1 so to avoid such things you should avoid list operations while iterating over it. – Mahesh Karia Nov 29 '17 at 05:47
  • Got it! So when a for loop iterates over a list it does not actually go over the "elements" in order, but over "index numbers" in order. I had not realized that, thanks for explaining. – Seeing what the fuss is about Nov 29 '17 at 19:02