Search code examples
pythonlistcycle

How to delete list elements while cycling the list itself without duplicate it


I lost a little bit of time in this Python for statement:

class MyListContainer:
    def __init__(self):
        self.list = []

    def purge(self):
        for object in self.list:
            if (object.my_cond()):
                self.list.remove(object)
        return self.list

container = MyListContainer()

# now suppose both obj.my_cond() return True
obj1 = MyCustomObject(par)
obj2 = MyCustomObject(other_par)

container.list = [obj1, obj2]

# returning not an empty list but [obj2]
container.purge()

It doesn't work as I expected because when the cycle in "purge" delete the first object in list the second one is shifted to the beginning of the list and the cycle is ended.

I solved duplicating self.list before the for cycle:

...
local_list = self.list[:]
for object in local_list:
...

I suppose that the for statement stop working because I'm changing the length of the original list. Can someone clarify this point ?

And is there a more "elegant" way to solve this problem ? If I have more than few elements inside the list, duplicating it every time does not seem a good idea.

Maybe the filter() function is the right one but i whish to have some other approach if any.

I'm a newbie.


To summarize your useful answers:

  • Never edit a list you are looping
  • Duplicate the list or use list comprehensions
  • Duplicating a list could not waste your memory or in this case who's mind about it

Solution

  • Filter (or list comprehension) IS the way to go. If you want to do it inplace, something like this would work:

    purge = []
    for i,object in enumerate(self.list):
        if object.mycond()
            purge.append(i)
    for i in reversed(purge):
        del self.list[i]
    

    Or alternatively, the purge list can be made with a comprehension, a shortcut version looks like:

    for i in reversed([ i for (i,o) in enumerate(self.list) if o.mycond() ]):
        del self.list[i]