Python
List Manipulation
Programming
Data Processing
Coding Techniques

Remove items from one list in another

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Removing items from one list based on values in another looks simple, but the correct approach depends on what "remove" means for your data. Sometimes you want to drop every matching value, sometimes you want to preserve duplicate counts, and sometimes order matters more than raw speed. Python supports all of these cases, but the cleanest solution changes with the requirement.

Use a List Comprehension for the Basic Case

If you want to keep items from source that do not appear in blocked, a list comprehension is the most readable starting point.

python
1source = [1, 2, 3, 4, 2, 5]
2blocked = [2, 5]
3
4result = [item for item in source if item not in blocked]
5print(result)

Output:

python
[1, 3, 4]

This preserves the original order of source and removes every value that exists in blocked. For short lists, that is often enough. The downside is performance: item not in blocked scans the second list each time, so the total cost grows quickly as both inputs get larger.

Use a Set for Faster Membership Tests

When the second collection is large, convert it to a set first. Membership checks against a set are usually much faster.

python
1source = [1, 2, 3, 4, 2, 5]
2blocked = [2, 5]
3
4blocked_set = set(blocked)
5result = [item for item in source if item not in blocked_set]
6print(result)

This produces the same result, but scales better for large inputs. The key tradeoff is semantic, not technical: using a set ignores duplicate counts in blocked. If blocked contains [2, 2], a set still treats that as one blocked value.

Handle Duplicate Counts with Counter

Sometimes you want multiset subtraction rather than "remove every matching value." For example, if source is [1, 2, 2, 3] and blocked is [2], you may want the result to be [1, 2, 3] rather than [1, 3]. In that case, track how many times each value should be removed.

python
1from collections import Counter
2
3source = [1, 2, 2, 3, 2]
4blocked = [2, 3]
5
6counts = Counter(blocked)
7result = []
8
9for item in source:
10    if counts[item] > 0:
11        counts[item] -= 1
12    else:
13        result.append(item)
14
15print(result)

Output:

python
[1, 2, 2]

This version preserves order and removes only as many occurrences as requested by blocked.

Remove In Place Only When You Need Mutation

If other code holds a reference to the original list object, you may need to mutate it in place rather than creating a new list. Slice assignment is the safest way to do that while keeping the same list identity.

python
1numbers = [1, 2, 3, 4, 2, 5]
2blocked = {2, 5}
3
4numbers[:] = [item for item in numbers if item not in blocked]
5print(numbers)

This changes the contents of numbers without creating a new external object. Avoid calling remove inside a loop over the same list, because that tends to skip elements or turn an easy task into fragile mutation logic.

Choose Based on Meaning, Not Just Speed

The best solution comes from the data rules:

  1. Use a list comprehension when the lists are small and clarity matters most.
  2. Use a set when you want fast membership tests and do not care about duplicate counts in the second list.
  3. Use Counter when duplicates in the removal list carry meaning.
  4. Use slice assignment only when you truly need to mutate the original list object.

Those distinctions matter more than memorizing one "Pythonic" answer.

Common Pitfalls

  • Using a plain list membership check on very large inputs and then being surprised by poor performance.
  • Converting the removal list to a set when duplicate counts are semantically important.
  • Calling list.remove inside a loop over the same list and skipping elements by accident.
  • Forgetting that a list comprehension creates a new list rather than mutating the original object.
  • Choosing a fast approach that changes ordering when the original sequence order must be preserved.

Summary

  • A list comprehension is the simplest way to remove all matching values while preserving order.
  • Converting the second collection to a set improves performance for large membership checks.
  • 'Counter is the right tool when removals should respect duplicate counts.'
  • Slice assignment lets you update a list in place without changing its identity.
  • Pick the approach that matches the data semantics first, then optimize if needed.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.