Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way to remove items from sequence in Python? [duplicate]

People also ask

How do you remove an element from a list while iterating in Python?

If you want to delete elements from a list while iterating, use a while-loop so you can alter the current index and end index after each deletion.

How do I remove unwanted items from a list in Python?

The remove() method takes a single element as an argument and removes it from the List. The item parameter is required, and any type (string, number, List) the element you want to remove. The remove() method only removes the given element from the List.

How do you remove multiple of the same value from a list in Python?

Remove Multiple elements from list by index range using del. Suppose we want to remove multiple elements from a list by index range, then we can use del keyword i.e. It will delete the elements in list from index1 to index2 – 1.

How do you remove duplicates without changing the order in Python?

If you want to preserve the order while you remove duplicate elements from List in Python, you can use the OrderedDict class from the collections module. More specifically, we can use OrderedDict. fromkeys(list) to obtain a dictionary having duplicate elements removed, while still maintaining order.


Two easy ways to accomplish just the filtering are:

  1. Using filter:

    names = filter(lambda name: name[-5:] != "Smith", names)

  2. Using list comprehensions:

    names = [name for name in names if name[-5:] != "Smith"]

Note that both cases keep the values for which the predicate function evaluates to True, so you have to reverse the logic (i.e. you say "keep the people who do not have the last name Smith" instead of "remove the people who have the last name Smith").

Edit Funny... two people individually posted both of the answers I suggested as I was posting mine.


You can also iterate backwards over the list:

for name in reversed(names):
    if name[-5:] == 'Smith':
        names.remove(name)

This has the advantage that it does not create a new list (like filter or a list comprehension) and uses an iterator instead of a list copy (like [:]).

Note that although removing elements while iterating backwards is safe, inserting them is somewhat trickier.


The obvious answer is the one that John and a couple other people gave, namely:

>>> names = [name for name in names if name[-5:] != "Smith"]       # <-- slower

But that has the disadvantage that it creates a new list object, rather than reusing the original object. I did some profiling and experimentation, and the most efficient method I came up with is:

>>> names[:] = (name for name in names if name[-5:] != "Smith")    # <-- faster

Assigning to "names[:]" basically means "replace the contents of the names list with the following value". It's different from just assigning to names, in that it doesn't create a new list object. The right hand side of the assignment is a generator expression (note the use of parentheses rather than square brackets). This will cause Python to iterate across the list.

Some quick profiling suggests that this is about 30% faster than the list comprehension approach, and about 40% faster than the filter approach.

Caveat: while this solution is faster than the obvious solution, it is more obscure, and relies on more advanced Python techniques. If you do use it, I recommend accompanying it with a comment. It's probably only worth using in cases where you really care about the performance of this particular operation (which is pretty fast no matter what). (In the case where I used this, I was doing A* beam search, and used this to remove search points from the search beam.)


Using a list comprehension

list = [x for x in list if x[-5:] != "smith"]

There are times when filtering (either using filter or a list comprehension) doesn't work. This happens when some other object is holding a reference to the list you're modifying and you need to modify the list in place.

for name in names[:]:
    if name[-5:] == 'Smith':
        names.remove(name)

The only difference from the original code is the use of names[:] instead of names in the for loop. That way the code iterates over a (shallow) copy of the list and the removals work as expected. Since the list copying is shallow, it's fairly quick.


filter would be awesome for this. Simple example:

names = ['mike', 'dave', 'jim']
filter(lambda x: x != 'mike', names)
['dave', 'jim']

Edit: Corey's list comprehension is awesome too.


names = filter(lambda x: x[-5:] != "Smith", names);