Question

Remove List Elements with LINQ in C#

csharp.netlinqlist

Question

Given a LINQ query that selects Author objects whose firstname is "Bob" from a List<Author>, how can the matching Author elements be removed from the original list?

var authors = from x in authorsList
              where x.firstname == "Bob"
              select x;

In other words, how can all authors whose first name equals "Bob" be deleted from authorsList? This is a simplified example.

Short Answer

You will learn the difference between querying a list and changing it, how to remove all items matching a condition with List<T>.RemoveAll, and how to avoid modifying a collection while it is being enumerated.

Concept

LINQ is primarily used to query data: it finds, transforms, groups, or orders values. A query such as:

var authors = authorsList.Where(author => author.FirstName == "Bob");

does not remove anything. It describes which items should be returned when the query is enumerated.

To change a List<T>, use methods provided by List<T>. For removing every item that matches a condition, use RemoveAll:

authorsList.RemoveAll(author => author.FirstName == "Bob");

RemoveAll accepts a predicate: a function that receives one item and returns true when that item should be removed. It changes the existing list and returns the number of removed items.

This distinction matters because a query result is not automatically a separate editable list, and changing the source list while a LINQ query is reading it can cause runtime errors or confusing behavior.

Mental Model

Think of a LINQ query as a search instruction written on paper: “Show me every author named Bob.” Writing that instruction does not remove authors from the bookshelf.

RemoveAll is the librarian carrying out a different instruction: “Walk through the shelf and remove every book whose author has the first name Bob.”

  • Where(...) identifies matching items.
  • RemoveAll(...) deletes matching items from a List<T>.
  • The predicate is the rule used to decide whether an item matches.
Take Quiz

Syntax and Examples

List<T>.RemoveAll has this signature:

int removedCount = list.RemoveAll(item => condition);
  • item => condition is a lambda expression.
  • Return true to remove the current item.
  • Return false to keep it.
  • The method returns how many items it removed.
public class Author
{
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";
}

var authorsList = new List<Author>
{
    new Author { FirstName = "Bob", LastName = "Smith" },
    new Author { FirstName = "Ana", LastName = "Jones" },
    new Author { FirstName = "Bob", LastName = "Taylor" }
};

int removed = authorsList.RemoveAll(author => author.FirstName == "Bob");

Console.WriteLine(removed);          // 2
Console.WriteLine(authorsList.Count); // 1

After the call, both authors named Bob are no longer in authorsList.

If names should be matched without considering capitalization, use StringComparison:

authorsList.RemoveAll(author =>
    string.Equals(author.FirstName, "Bob", StringComparison.OrdinalIgnoreCase));

This matches "Bob", "BOB", and "bob".

Step by Step Execution

Consider this list:

var authorsList = new List<Author>
{
    new Author { FirstName = "Bob", LastName = "Smith" },
    new Author { FirstName = "Ana", LastName = "Jones" },
    new Author { FirstName = "Bob", LastName = "Taylor" }
};

int removed = authorsList.RemoveAll(author => author.FirstName == "Bob");

Execution proceeds as follows:

  1. RemoveAll examines the first author, Bob Smith.
  2. author.FirstName == "Bob" is true, so Bob Smith is removed.
  3. It examines Ana Jones.
  4. The condition is false, so Ana stays in the list.
  5. It examines Bob Taylor.
  6. The condition is true, so Bob Taylor is removed.
  7. The method returns 2, because two elements were deleted.

The resulting list contains only Ana Jones:

// authorsList contains: Ana Jones
// removed is: 2

Real World Use Cases

Removing matching list items is common in application code:

  • Shopping carts: Remove unavailable products from an in-memory cart.
  • Notification queues: Delete expired notifications before displaying them.
  • Import cleanup: Remove invalid records after validating a CSV or API response.
  • User preferences: Remove disabled features or revoked permissions from a local collection.
  • Game state: Remove defeated enemies, expired effects, or disconnected players.

Example: remove expired access tokens from a list.

int removed = tokens.RemoveAll(token => token.ExpiresAt <= DateTime.UtcNow);
Take Quiz

Real Codebase Usage

In production code, developers usually make the removal condition explicit and safe.

Use a named predicate when the rule has meaning

bool IsInactiveUser(User user) => !user.IsActive;

int removed = users.RemoveAll(IsInactiveUser);

A named method is easier to test and read when the condition becomes more complex.

Validate input before modifying a list

if (authorsList is null)
{
    throw new ArgumentNullException(nameof(authorsList));
}

authorsList.RemoveAll(author => author.FirstName == "Bob");

Use a query when you need a new collection instead

Sometimes the requirement is not “change this list,” but “produce a filtered list.” In that case, keep nonmatching authors:

List<Author> remainingAuthors = authorsList
    .Where(author => author.FirstName != "Bob")
    .ToList();

This creates a new list. The original authorsList remains unchanged.

For databases, do not assume RemoveAll saves data

RemoveAll only changes an in-memory List<T>. If authors are stored in a database, use the database framework's deletion workflow and save or execute the change according to that framework.

Common Mistakes

Removing items inside a foreach loop

This is unsafe because foreach is enumerating the list while the list is changed.

// Broken: typically throws InvalidOperationException.
foreach (Author author in authorsList)
{
    if (author.FirstName == "Bob")
    {
        authorsList.Remove(author);
    }
}

Use RemoveAll instead:

authorsList.RemoveAll(author => author.FirstName == "Bob");

Expecting Where to modify the original list

// This only creates a query. Nothing is deleted.
authorsList.Where(author => author.FirstName != "Bob");

Assign the filtered result to a new list if you want a copy:

var remaining = authorsList
    .Where(author => author.FirstName != "Bob")
    .ToList();

Calling Remove only once

// Removes one matching object instance, not every Bob.
authorsList.Remove(new Author { FirstName = "Bob" });

Remove removes one item based on equality rules. Use RemoveAll when the rule is a condition and multiple matches may exist.

Comparing possibly null strings carelessly

If FirstName can be null, this can fail:

// Broken if author.FirstName is null.
authorsList.RemoveAll(author => author.FirstName.Equals("Bob"));

Put the known non-null value first, or use string.Equals:

authorsList.RemoveAll(author => author.FirstName == "Bob");

Using a query result while changing its source

var bobs = authorsList.Where(author => author.FirstName == "Bob");

// Avoid enumerating bobs while removing from authorsList.
foreach (var author in bobs)
{
    authorsList.Remove(author);
}

If RemoveAll cannot express the removal rule, materialize the query first with ToList(), then remove items from the original list.

Comparisons

ApproachChanges the original List<T>?Best use
RemoveAll(predicate)YesRemove every item matching a condition.
Remove(item)YesRemove one known item instance or equal value.
Where(predicate)NoQuery matching items.
Where(...).ToList()NoCreate a separate filtered list.
Clear()YesRemove every item without a condition.

RemoveAll versus filtering into a new list:

// Mutates the existing list.
authorsList.RemoveAll(author => author.FirstName == "Bob");

// Does not mutate authorsList; creates a new list instead.
var remainingAuthors = authorsList
    .Where(author => author.FirstName != "Bob")
    .ToList();

Choose RemoveAll when other code should observe the same list after it has been cleaned. Choose Where(...).ToList() when preserving the original list is important.

Take Quiz

Cheat Sheet

// Remove all matching items and get the number removed.
int count = list.RemoveAll(item => condition);

// Remove authors named Bob.
authorsList.RemoveAll(author => author.FirstName == "Bob");

// Case-insensitive match.
authorsList.RemoveAll(author =>
    string.Equals(author.FirstName, "Bob", StringComparison.OrdinalIgnoreCase));

// Keep the original list; create a filtered copy.
var copy = authorsList.Where(author => author.FirstName != "Bob").ToList();
  • RemoveAll is a List<T> method, not a general LINQ operator.
  • A predicate returning true means remove this item.
  • RemoveAll changes the list in place.
  • Its return value is the number of removed elements.
  • Do not call Remove on a list inside a foreach that iterates over that same list.

FAQ

Does LINQ Where remove items from a list?

No. Where selects items; it does not change the source collection.

What is the easiest way to delete all matching items from List<T>?

Use RemoveAll with a predicate:

list.RemoveAll(item => item.IsDeleted);

What does RemoveAll return in C#?

It returns an int containing the number of items removed.

Can I use RemoveAll with a LINQ query result?

RemoveAll belongs to List<T>, so call it on the original list. A LINQ query is normally an IEnumerable<T>, which does not provide RemoveAll.

Why does removing in foreach throw an exception?

Changing a list invalidates its active enumerator. foreach detects that change and usually throws InvalidOperationException.

How do I remove items without changing the original list?

Use Where to keep the desired items and materialize them with ToList().

Is RemoveAll case-sensitive for string comparisons?

The comparison is determined by your predicate. == "Bob" is case-sensitive; use string.Equals with StringComparison.OrdinalIgnoreCase for case-insensitive matching.

Take Quiz

Mini Project

Description

Build a small in-memory cleanup routine for an author catalog. The routine removes authors with a selected first name and reports how many records were removed. This mirrors maintenance tasks often performed on temporary data loaded from files, APIs, or user input.

Goal

Remove every author named Bob from a List<Author> and print the remaining authors.

Requirements

Define an Author class with first and last name properties. Create a list containing at least two authors named Bob and one author with a different name. Remove all authors whose first name is Bob using RemoveAll. Store and display the number of removed authors. Display the authors that remain in the list.

Take Quiz

Keep learning

Related questions

View more