6

How to remove both occurrences of 333 from the below list?

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]

I typed the below script in the Python 2.7 command line

for num in a:
    if num == 333:
       a.remove(num)

But only the first occurrence of 333 is removed

 >>> a
 [-1, 1, 66.25, 333, 1234.5]

How to remove all the occurrences of the same element? I want to be able to specify the element of which I want all the occurrences be removed and get a new list by the same name or another

Varuna
  • 1,343
  • 3
  • 15
  • 30
  • 2
    Posting the same question multiple times does not change the fact that it is *still* a duplicate of an answered question. – David Hoelzer Dec 05 '15 at 19:00

4 Answers4

7

Use a list comprehension here:

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> [item for item in a if item != 333]
[-1, 1, 66.25, 1234.5]

Your approach didn't work because you're modifying a list while iterating over it.

for num in a[:]:  #iterate over a shallow copy
    if num == 333:
       a.remove(num)

To get a list of just unique items, use a set:

>>> seen = set()
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> [item for item in a if item not in seen and not seen.add(item)]
[-1, 1, 66.25, 333, 1234.5]

If order doesn't matter:

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> list(set(a))
[66.25, 1, 333, -1, 1234.5]
Community
  • 1
  • 1
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • That's linear time and linear space. Consider `while 333 in a: a.remove(333)` for quadratic time and no extra space – inspectorG4dget Sep 05 '13 at 06:16
  • 2
    @inspectorG4dget `for _ in range(a.count(333)): a.remove(333)` is slightly more efficient. – Bakuriu Sep 05 '13 at 06:21
  • @Bakuriu: you're right. Yours is 2x linear and my suggestion is quadratic! – inspectorG4dget Sep 05 '13 at 06:21
  • No, it's the exact same asymptotic runtime. – user2357112 Sep 05 '13 at 06:24
  • What is linear time and quadratic time in this context? And Why when the for loop cycles through the list from the start to finish and on each encounter of 333 it can remove 333, But why doesn't that take place here? can you please explain? – Varuna Sep 05 '13 at 06:26
  • 1
    @Varuna That has been explained here: [loop forgets to remove some items](http://stackoverflow.com/questions/17299581/loop-forgets-to-remove-some-items) – Ashwini Chaudhary Sep 05 '13 at 06:27
  • Why not just `list(set(a))` – Aesthete Sep 05 '13 at 06:32
  • @Aesthete That won't preserve the order. – Ashwini Chaudhary Sep 05 '13 at 06:34
  • @user2357112 That's what I meant by "slightly more efficient". My version does only half the operations. Anyway it's wrong to speak of "quadratic time" or "linear time" since the time depends on the number of occurrences of `333` in `a`. The asymptotic time is `O(kn)` which can be linear if `k = O(1)`, it can be quasi linear if `k = O(log(n))` and it can be quadratic if `k = O(n)`. – Bakuriu Sep 05 '13 at 06:35
  • @Ashwini Chaudhary-this >>> a = [-1, 1, 66.25, 333, 333, 1234.5] >>> [item for item in a if item != 333] doesn't serve you with anew list is it? – Varuna Sep 05 '13 at 08:49
  • 1
    @Varuna It returns a new list, if you want to modify the same list then use the in-efficient `list.remove` method. To modify the object `a` in-place use: `a[:] = [item for item in a if item != 333]` , this would create a new list as well but the id() of `a` won't change. – Ashwini Chaudhary Sep 05 '13 at 08:52
6

Since you ask how to remove elements, I would re-assign the list as a slice.

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> a[:] = [item for item in a if item != 333]

This will create a new list in memory, normally thats acceptable, but if you want to avoid this without calling remove, which would check against the same items multiple times.

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> for i in range(len(a) - 1, -1, -1):  # iterate over reversed indices's
>>>     if a[i] == 333:
>>>         del a[i]

This has the advantage that it removes the last items first (which is faster in CPython).

Note that removing items from the beginning-middle of lists in Python is not so optimal. no matter which method you use (del list[index] or list.remove), if the list is large and many items have to be removed this means Python needs to resize the list a lot, in that case I would recommend using list-comprehension to make a new list.

ideasman42
  • 42,413
  • 44
  • 197
  • 320
  • Just curious, but is it necessary to add the `[:]` after `a` on the second line? When I tested it, removing the `[:]` doesn't make a difference... – modulitos Jul 30 '14 at 06:30
  • 3
    @Lucas - Yes, it makes a difference, since the question asks to remove from a list, removing the `[:]` will assign a new variable rather then modifying the list in-place, Thats an important difference if the list is referenced elsewhere. – ideasman42 Jul 30 '14 at 06:34
5

I think what you want here is something like this

a = [-1, 1, 66.25, 333, 333, 1234.5]
a = [el for el, count in collections.Counter(a).items() if count == 1]

This will remove elements from the list of any value, providing they occur more than once.

Aesthete
  • 18,622
  • 6
  • 36
  • 45
  • This actually answers the question. The other answers only remove the occurrence of 333, not a general solution. – justhalf Sep 05 '13 at 06:27
  • I don't think this is what the OP is looking for; I think he just worded the title awkwardly. – user2357112 Sep 05 '13 at 06:27
  • 2
    You can see that last line of the question: `How to remove all the occurrences of the same element?` – justhalf Sep 05 '13 at 06:28
  • 4
    But OP explicitly used : `if num == 333` in their code. I'd use a set or uniqueverseen recipe from itertools instead of `collections.Counter` because that would preserve the order as well. – Ashwini Chaudhary Sep 05 '13 at 06:30
  • The title exactly expresses what I have to ask, In the list there are two occurrences of the same element in this case 333.I used 333 just to test to see whether my method was working. In the description here http://docs.python.org/2/tutorial/datastructures.html, it says list.remove(x) removes the first encounter of an element.I thought by using remove() an element gets removed and during the next iteration of the for loop the next occurence of the same element will be encountered and will be removed – Varuna Sep 05 '13 at 07:02
  • @Varuna - **"How to remove all the occurrences of the same element in a Python list?"** - This will do that, and only that - like you asked. – Aesthete Sep 05 '13 at 07:07
0

in following code 4 number is removed from [5,3,4,7,8,4] list

>>> def pp(x):
...     if x==4:
...             return False
...     else:
...             return True
... 
>>> filter(pp, [5,3,4,7,8,4])

output

[5, 3, 7, 8]
navyad
  • 3,752
  • 7
  • 47
  • 88