176

In python, I know how to remove items from a list:

item_list = ['item', 5, 'foo', 3.14, True]
item_list.remove('item')
item_list.remove(5)

The above code removes the values 5 and 'item' from item_list. But when there is a lot of stuff to remove, I have to write many lines of:

item_list.remove("something_to_remove")

If I know the index of what I am removing, I use:

del item_list[x]

where x is the index of the item I want to remove.

If I know the index of all of the numbers that I want to remove, I'll use some sort of loop to del the items at the indices.

But what if I don't know the indices of the items I want to remove?

I tried item_list.remove('item', 'foo'), but I got an error saying that remove only takes one argument.

Is there a way to remove multiple items from a list in a single statement?

P.S. I've used del and remove. Can someone explain the difference between these two, or are they the same?

martineau
  • 119,623
  • 25
  • 170
  • 301
RandomCoder
  • 2,071
  • 4
  • 12
  • 17
  • 3
    To answer your second question: `del` deletes an item by its index. The `remove` function of a list finds an item's index and then calls `del` on that index. – Aaron Christiansen Mar 28 '16 at 18:47
  • Possible duplicate of [Deleting multiple elements from a list](https://stackoverflow.com/questions/497426/deleting-multiple-elements-from-a-list) – R7L208 Jun 20 '17 at 20:40
  • Can your list contain duplicates? `l.remove(value)` only removes the first occurrence of value. Consider the list ['a', 'b', 'a', 'c', 'b', 'a']. If you want to remove all occurrences of a value, it's less grief to use a list comprehension than iteratively doing `del` or `.remove()`. – smci Mar 10 '22 at 18:28

10 Answers10

265

In Python, creating a new object e.g. with a list comprehension is often better than modifying an existing one:

item_list = ['item', 5, 'foo', 3.14, True]
item_list = [e for e in item_list if e not in ('item', 5)]

... which is equivalent to:

item_list = ['item', 5, 'foo', 3.14, True]
new_list = []
for e in item_list:
    if e not in ('item', 5):
        new_list.append(e)
item_list = new_list

In case of a big list of filtered out values (here, ('item', 5) is a small set of elements), using a set is faster as the in operation is O(1) time complexity on average. It's also a good idea to build the iterable you're removing first, so that you're not creating it on every iteration of the list comprehension:

unwanted = {'item', 5}
item_list = [e for e in item_list if e not in unwanted]

A bloom filter is also a good solution if memory is not cheap.

smci
  • 32,567
  • 20
  • 113
  • 146
aluriak
  • 5,559
  • 2
  • 26
  • 39
  • Is the set optimised in python 2 or is it only optimised in python 3? By that I mean is the set created only once when the bytecode is generated? – Har Feb 15 '17 at 08:22
  • Set is, [by definition](https://wiki.python.org/moin/TimeComplexity), optimized for `in` operation. See [this benchmarks](https://github.com/Aluriak/__contains__-comparison) for comparisons of the four primitive data structures. Yes, the set is built at each loop, as suggested [here](https://gist.github.com/Aluriak/01c3d100cb44ef048c00854c6f439642), consequently save the set in a dedicated variable to use in the generator expression can save time. – aluriak Feb 15 '17 at 11:03
  • 1
    I came across this question. I just want to point out that the original list may contain duplicates and the intention to remove element may simply be remove as many element as in the remove-list. The above method will ended up eliminating all duplicates. Not necessary the right behavior. – some user Jan 03 '21 at 01:07
  • @aluriak benchmarks as of python 3.9.7 seem to give different results. if you have a complex iterable you're filtering by, then pre-building the iterable is best, but set vs. list gives about the same result. for simple iterables (simple membership of small iterables), then set does outperform lists. see [this adaptation of your experiment](https://gist.github.com/chucklesoclock/5269aa5e42b6cc51ae8aece07f1381a0). – Charlie G Sep 21 '21 at 21:58
  • When you introduce an important new concept like a list comprehension that OP has almost surely never seen before and doesn't exist in most languages, name it (and hyperlink it). Don't just show them the code. – smci Mar 10 '22 at 19:04
50

You can do it in one line by converting your lists to sets and using set.difference:

item_list = ['item', 5, 'foo', 3.14, True]
list_to_remove = ['item', 5, 'foo']

final_list = list(set(item_list) - set(list_to_remove))

Would give you the following output:

final_list = [3.14, True]

Note: this will remove duplicates in your input list and the elements in the output can be in any order (because sets don't preserve order). It also requires all elements in both of your lists to be hashable.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Aakash Goel
  • 982
  • 7
  • 12
2

I'm reposting my answer from here because I saw it also fits in here. It allows removing multiple values or removing only duplicates of these values and returns either a new list or modifies the given list in place.


def removed(items, original_list, only_duplicates=False, inplace=False):
    """By default removes given items from original_list and returns
    a new list. Optionally only removes duplicates of `items` or modifies
    given list in place.
    """
    if not hasattr(items, '__iter__') or isinstance(items, str):
        items = [items]

    if only_duplicates:
        result = []
        for item in original_list:
            if item not in items or item not in result:
                result.append(item)
    else:
        result = [item for item in original_list if item not in items]

    if inplace:
        original_list[:] = result
    else:
        return result

Docstring extension:

"""
Examples:
---------

    >>>li1 = [1, 2, 3, 4, 4, 5, 5]
    >>>removed(4, li1)
       [1, 2, 3, 5, 5]
    >>>removed((4,5), li1)
       [1, 2, 3]
    >>>removed((4,5), li1, only_duplicates=True)
       [1, 2, 3, 4, 5]

    # remove all duplicates by passing original_list also to `items`.:
    >>>removed(li1, li1, only_duplicates=True)
      [1, 2, 3, 4, 5]

    # inplace:
    >>>removed((4,5), li1, only_duplicates=True, inplace=True)
    >>>li1
        [1, 2, 3, 4, 5]

    >>>li2 =['abc', 'def', 'def', 'ghi', 'ghi']
    >>>removed(('def', 'ghi'), li2, only_duplicates=True, inplace=True)
    >>>li2
        ['abc', 'def', 'ghi']
"""

You should be clear about what you really want to do, modify an existing list, or make a new list with the specific items missing. It's important to make that distinction in case you have a second reference pointing to the existing list. If you have, for example...

li1 = [1, 2, 3, 4, 4, 5, 5]
li2 = li1
# then rebind li1 to the new list without the value 4
li1 = removed(4, li1)
# you end up with two separate lists where li2 is still pointing to the 
# original
li2
# [1, 2, 3, 4, 4, 5, 5]
li1
# [1, 2, 3, 5, 5]

This may or may not be the behaviour you want.

Darkonaut
  • 20,186
  • 7
  • 54
  • 65
2

You can use filterfalse function from itertools module

Example

import random
from itertools import filterfalse

random.seed(42)

data = [random.randrange(5) for _ in range(10)]
clean = [*filterfalse(lambda i: i == 0, data)]
print(f"Remove 0s\n{data=}\n{clean=}\n")


clean = [*filterfalse(lambda i: i in (0, 1), data)]
print(f"Remove 0s and 1s\n{data=}\n{clean=}")

Output:

Remove 0s
data=[0, 0, 2, 1, 1, 1, 0, 4, 0, 4]
clean=[2, 1, 1, 1, 4, 4]

Remove 0s and 1s
data=[0, 0, 2, 1, 1, 1, 0, 4, 0, 4]
clean=[2, 4, 4]
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
1

You Can use this -

Suppose we have a list, l = [1,2,3,4,5]

We want to delete last two items in a single statement

del l[3:]

We have output:

l = [1,2,3]

Keep it Simple

Krishna Singhal
  • 631
  • 6
  • 9
0

But what if I don't know the indices of the items I want to remove?

I do not exactly understand why you do not like .remove but to get the first index corresponding to a value use .index(value):

ind=item_list.index('item')

then remove the corresponding value:

del item_list[ind]

.index(value) gets the first occurrence of value, and .remove(value) removes the first occurrence of value. You are welcome.

nocturne
  • 627
  • 3
  • 12
  • 39
aless80
  • 3,122
  • 3
  • 34
  • 53
0

I don't know why everyone forgot to mention the amazing capability of sets in python. You can simply cast your list into a set and then remove whatever you want to remove in a simple expression like so:

>>> item_list = ['item', 5, 'foo', 3.14, True]
>>> item_list = set(item_list) - {'item', 5}
>>> item_list
{True, 3.14, 'foo'}
>>> # you can cast it again in a list-from like so
>>> item_list = list(item_list)
>>> item_list
[True, 3.14, 'foo']
Anwarvic
  • 12,156
  • 4
  • 49
  • 69
0

Suppose we have my_list as below. We would like to remove the duplicated 0's from our list. By using remove(), only one 0 can be removed, whereas the next code can remove all the duplicated 0's at once:

my_list = [1, 2, 3, 0, 0, 0, 3, 4]
list(filter(lambda a: a != 0, my_list))

output:

[1, 3, 3, 4]
A. chahid
  • 184
  • 2
  • 5
0

we can remove multiple elements

list1=[1,2,3,4,5,200,30]

del list1[1:3]

print(list1)

[1,4,5,200,30]

0

You can combine the numpy array and set function to end up with a new array that only shows the elements you want to keep.

import numpy as np
# given an array A:
A = [5,78,423,87,45,78,4]
# first convert your array to a numpy array
A_np = np.array(A)
# specify the indices you want to remove
inds_to_be_deleted = [3,5]
# find the remaining indices using set function
remaining_inds = list(set(range(len(A)))-set(inds_to_be_deleted))
# the new array will only contain the elements at the remaining indices
A_new = A_np[remaining_inds]

which gives you this output: array([ 5, 78, 423, 45, 4])

Ayman_Negm
  • 21
  • 3