list manipulation
iteration techniques
python programming
loop removal
coding tips

How to remove items from a list while iterating?

Master System Design with Codemia

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

Introduction

Removing items from a list while iterating over it can produce skipped elements or incorrect behavior because indices shift during mutation. This is a classic bug in Python and many other languages. Safe patterns include building a new list, iterating over a copy, or iterating backwards when in-place removal is required.

Why Direct Removal Fails

Example of problematic pattern:

python
1nums = [1, 2, 3, 4, 5, 6]
2for n in nums:
3    if n % 2 == 0:
4        nums.remove(n)
5print(nums)

Result may be unexpected because list contents move left while iterator index moves forward.

Preferred Pattern: List Comprehension

Create filtered list in one expression.

python
nums = [1, 2, 3, 4, 5, 6]
nums = [n for n in nums if n % 2 != 0]
print(nums)  # [1, 3, 5]

This is clear, fast, and usually best for readability.

In-Place Update Without New Object

If references to original list must remain valid:

python
nums = [1, 2, 3, 4, 5, 6]
nums[:] = [n for n in nums if n % 2 != 0]
print(nums)

Slice assignment mutates existing list object in place.

Iterating Over a Copy

If you prefer explicit remove operations:

python
1nums = [1, 2, 3, 4, 5, 6]
2for n in nums[:]:
3    if n % 2 == 0:
4        nums.remove(n)
5print(nums)

This works because iteration uses snapshot copy.

Reverse Index Iteration

When index operations are needed, iterate backwards.

python
1nums = [1, 2, 3, 4, 5, 6]
2for i in range(len(nums) - 1, -1, -1):
3    if nums[i] % 2 == 0:
4        del nums[i]
5print(nums)

Backward traversal avoids index shift problems.

Filtering Complex Objects

Pattern applies to object lists too.

python
1users = [
2    {"name": "A", "active": True},
3    {"name": "B", "active": False},
4]
5active_users = [u for u in users if u["active"]]
6print(active_users)

For large pipelines, generator expressions can reduce intermediate allocations.

Performance Notes

  • list comprehension is often fastest and clearest
  • repeated .remove inside loop can be costly
  • in-place mutation may be necessary for shared references

Choose based on semantics first, micro performance second.

Removing While Iterating in Other Languages

The same hazard appears in JavaScript and Java collections. The root issue is modifying structure while traversal cursor assumes stable layout.

Stable In-Place Partition Pattern

For performance-sensitive lists, two-pointer partition can avoid repeated removes.

python
1def keep_positive(nums):
2    w = 0
3    for r in range(len(nums)):
4        if nums[r] > 0:
5            nums[w] = nums[r]
6            w += 1
7    del nums[w:]
8
9arr = [3, -1, 2, -5, 7]
10keep_positive(arr)
11print(arr)

This mutates in place with linear complexity and no extra list allocation beyond existing storage.

Choose one standard removal pattern for your codebase and document it in style guides.

Document this pattern for team consistency.

Validate with realistic test cases before deployment.

Common Pitfalls

  • Removing from list while iterating same list directly.
  • Confusing in-place mutation with reassignment semantics.
  • Using .remove when duplicate values require index-aware deletion.
  • Writing complex mutation loops where simple filtering is enough.
  • Ignoring readability in favor of clever but fragile loop logic.

Summary

  • Direct removal during iteration is unsafe due to index shifting.
  • Prefer list comprehensions for most filtering tasks.
  • Use slice assignment for in-place updates when needed.
  • Backward index iteration is safe for in-place deletes.
  • Pick the clearest pattern that matches mutation requirements.

Course illustration
Course illustration

All Rights Reserved.