Python
list comprehension
programming
coding tips
data manipulation

How to remove multiple items from a list in just one statement?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Removing multiple items from a Python list is one of those tasks that looks simple but hides real performance traps. The naive approach -- looping and calling remove() one at a time -- is both verbose and slow because each remove() scans the entire list. Understanding the one-statement alternatives matters because choosing the wrong technique on a large dataset can turn a millisecond operation into one that takes minutes.

List Comprehension Filter

The most Pythonic way to remove multiple items is a list comprehension that keeps only the values you want. This works because it is easier to think about what to keep than what to discard, and it produces a clean new list in a single expression.

python
1original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2unwanted = {2, 4, 6, 8}  # use a set for O(1) lookup
3
4cleaned = [x for x in original if x not in unwanted]
5print(cleaned)  # [1, 3, 5, 7, 9, 10]

Notice that unwanted is a set, not a list. This is the single most important performance detail in this pattern. Checking membership in a set is O(1) on average, while checking membership in a list is O(n). For a list of 100,000 items with 10,000 to remove, this difference is enormous.

Set Difference

When your list contains unique elements and order does not matter, set arithmetic gives you the most concise syntax.

python
1original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2to_remove = {2, 4, 6, 8}
3
4result = list(set(original) - to_remove)
5print(sorted(result))  # [1, 3, 5, 7, 9, 10]

The tradeoff is clear: you lose the original ordering and any duplicate values. If either of those matters to your use case, stick with the list comprehension approach.

Using the filter() Function

The built-in filter() function offers a functional programming style alternative. It takes a predicate function and an iterable, returning only the elements where the predicate is True.

python
1original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2unwanted = {2, 4, 6, 8}
3
4cleaned = list(filter(lambda x: x not in unwanted, original))
5print(cleaned)  # [1, 3, 5, 7, 9, 10]

This is functionally identical to the list comprehension version. Most Python developers prefer the comprehension for readability, but filter() can be useful when you already have a named predicate function to pass in.

Removing by Index

Sometimes you need to remove items at specific positions rather than by value. This requires a different strategy because deleting an element shifts all subsequent indices.

The safest one-statement approach is to build a new list that skips the unwanted indices:

python
1original = ['a', 'b', 'c', 'd', 'e', 'f']
2indices_to_remove = {1, 3, 4}  # remove 'b', 'd', 'e'
3
4cleaned = [v for i, v in enumerate(original) if i not in indices_to_remove]
5print(cleaned)  # ['a', 'c', 'f']

If you must modify the list in place (for example, when other references point to the same list object), delete from the highest index to the lowest so that earlier indices remain valid:

python
1original = ['a', 'b', 'c', 'd', 'e', 'f']
2indices_to_remove = [1, 3, 4]
3
4for i in sorted(indices_to_remove, reverse=True):
5    del original[i]
6
7print(original)  # ['a', 'c', 'f']

For NumPy arrays, numpy.delete handles this natively:

python
1import numpy as np
2
3arr = np.array([10, 20, 30, 40, 50])
4cleaned = np.delete(arr, [1, 3])
5print(cleaned)  # [10 30 50]

Removing by Value with In-Place Mutation

If you need to modify the original list object rather than create a new one, slice assignment with a comprehension is the one-liner:

python
1original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2unwanted = {2, 4, 6, 8}
3
4original[:] = [x for x in original if x not in unwanted]
5print(original)  # [1, 3, 5, 7, 9, 10]

The original[:] = ... syntax replaces the contents of the existing list object in place. Any other variable referencing the same list will see the updated contents. This distinction matters when you pass lists to functions or store them in shared data structures.

Performance Comparison

The reason the choice of data structure for unwanted matters so much comes down to algorithmic complexity. Here is a practical comparison:

python
1import time
2
3big_list = list(range(100_000))
4to_remove_list = list(range(0, 100_000, 3))  # every 3rd element
5to_remove_set = set(to_remove_list)
6
7# List membership check: O(n) per lookup
8start = time.time()
9result = [x for x in big_list if x not in to_remove_list]
10print(f"List lookup: {time.time() - start:.3f}s")
11
12# Set membership check: O(1) per lookup
13start = time.time()
14result = [x for x in big_list if x not in to_remove_set]
15print(f"Set lookup: {time.time() - start:.3f}s")

On a typical machine, the list lookup version takes several seconds while the set version completes in milliseconds. The lesson is simple: always convert your removal targets to a set before filtering.

MethodPreserves OrderIn-PlaceTime Complexity
List comprehension + setYesNoO(n)
Slice assignment + setYesYesO(n)
Set differenceNoNoO(n)
filter() + setYesNoO(n)
Repeated list.remove()YesYesO(n * k)

In this table, n is the length of the original list and k is the number of items to remove.

Common Pitfalls

  • Using a list instead of a set for the removal targets: This turns an O(n) operation into O(n * k) because each in check scans the entire removal list.
  • Modifying a list while iterating over it: Deleting elements during a forward loop skips items and produces wrong results. Always iterate in reverse or build a new list.
  • Forgetting that list comprehension creates a new object: If other variables reference the original list, they will not see the changes. Use original[:] = ... for in-place mutation.
  • Using set difference when order matters: set(original) - to_remove discards the original ordering and eliminates duplicates, which silently corrupts data if you did not expect it.
  • Calling remove() in a loop for duplicate values: list.remove() only deletes the first occurrence. If the value appears multiple times, you need multiple calls or a comprehension-based approach.

Summary

  • List comprehension with a set of unwanted values ([x for x in lst if x not in bad_set]) is the standard one-statement pattern for removing multiple items.
  • Always convert your removal targets to a set for O(1) membership checks.
  • Use original[:] = ... when you need in-place mutation that propagates to all references.
  • Set difference is the most concise option but sacrifices ordering and deduplicates your data.
  • For index-based removal, enumerate and skip unwanted indices, or delete in reverse order to keep indices stable.
  • Avoid calling list.remove() in a loop -- it is both slower and more error-prone than any of the one-statement alternatives.

Course illustration
Course illustration

All Rights Reserved.