user300
user300

Reputation: 465

Remove evens from a list with del function

I'm not allowed to use the .remove() function to delete even numbers in a list and the specs say: You will be modifying the original list, so its id() must not change.This is what I have:

def remove_evens(xs):
    for i in range(len(xs)):
        for x in xs:
            if x%2==0:
                del xs[i]
    return xs

If for instance I test the function and I input xs=[1,2,3,4,5] it returns [3,5]. I don't know why the 1 doesn't return

Upvotes: 3

Views: 737

Answers (3)

Shashank
Shashank

Reputation: 13869

Maybe something like this will work for you.

>>> xs = [1,5,123,6,2,34]
>>> id(xs)
35519496L
>>> lastidx = len(xs) - 1
>>> for i, x in enumerate(reversed(xs)):
...     if x%2==0:
...         del xs[lastidx-i]
...
>>> xs
[1, 5, 123]
>>> id(xs)
35519496L

It's linear time because it iterates over the list only once. When you iterate over a list backwards, you can freely delete elements without modifying the indexes of future elements that you haven't looped over yet. Also the id of the list does not change with this method, as you can see. :)

Here is another way to do the same thing with only range/xrange and len.

>>> xs = [1,5,123,6,2,34]
>>> for i in range(len(xs)-1,-1,-1):
...     if xs[i]%2==0:
...         del xs[i]
...
>>> xs
[1, 5, 123]

Both ways do the same thing so whichever way you decide to use is a matter of preference/personal style.

Upvotes: 2

Simeon Visser
Simeon Visser

Reputation: 122336

That's because you're iterating over the list and modifying it at the same time. This means the index i isn't always correct because you've just removed an element from the list. This leads to skipping elements and that's why the 1 isn't in there.

You can do:

def remove_evens(xs):
    return [x for x in xs if x % 2 != 0]

This uses a list comprehension to create a list without the even numbers. If you need to modify the existing list you can do:

def remove_evens(xs):
    to_remove = []
    for i, x in enumerate(xs):
        if x % 2 == 0:
            to_remove.append(i)
    for j in to_remove:
        del xs[j]
    return xs

This creates a list to_remove which keeps track of the positions of the elements that need to be removed.

Upvotes: 2

inspectorG4dget
inspectorG4dget

Reputation: 113905

It’s a bad idea to delete items from a list as you are iterating over the list.

Here’s a few ways in which you can achieve your goal:

>>> def deleteEvens(L):
...   dels = []
...   for i,x in enumerate(L):
...     if not x%2:
...       dels.append(i)
...   for d in dels[::-1]:
...     L.pop(d)
... 
>>> L
[1, 2, 3, 4, 5]
>>> deleteEvens(L)
>>> L
[1, 3, 5]

OR

>>> L = [1,2,3,4,5]
>>> odds = [i for i in L if i%2]
>>> odds
[1, 3, 5]

OR

>>> def delEvens(L):
...   dels = []
...   for i,x in enumerate(L):
...     if not x%2:
...       dels.append(i)
...   for d in dels[::-1]:
...     del L[d]
... 
>>> L = [1,2,3,4,5]
>>> delEvens(L)
>>> L
[1, 3, 5]

Upvotes: 0

Related Questions