gurehbgui
gurehbgui

Reputation: 14684

what is the most performant way to delete elements from a List<T> in c# while doing a foreach?

i want to know what is the best way to delete elements from a List in c# while doing a foreach.

here is a code sample. first i create a list with some elements and then delte one:

List<int> foo = new List<int>();
foo.Add(1);
foo.Add(2);
foo.Add(3);
foreach (int i in foo)
{
    if (i==2)
    {
        foo.Remove(i);
    }
}

when i run this, i get a InvalidOperationException but how to solve this with a performant way?

Upvotes: 0

Views: 411

Answers (9)

Rob
Rob

Reputation: 4350

You can't edit a list while you're iterating it.

Consider:

List<int> foo;
int[] bar = foo.ToArray();

foreach(int i in bar)
{
    if (i == 2)
    {
        foo.Remove(i);
    }
}

But beware: you should walk this list backwards, because removing an item from the foo list will mean the bar list no longer aligns with it. (If you don't walk backwards, you'll have to keep track of the count of removals and adjust the index passed to the remove call!)

Upvotes: 0

Thomas Levesque
Thomas Levesque

Reputation: 292405

Why do you need a loop?

foo.Remove(2);

Upvotes: 4

Petar Petkov
Petar Petkov

Reputation: 1479

foo.RemoveAll(x => x == 2);

In case you decide not to use for and foreach ;)

Upvotes: 9

Tesserex
Tesserex

Reputation: 17314

If you want to remove arbitrary elements, and not just one, you can use RemoveAll and specify a predicate:

foo.RemoveAll(element => (element == 2));

Upvotes: 1

Anthony Pegram
Anthony Pegram

Reputation: 126804

I assume your actual use case is more complicated than what you have laid out. So let's assume you actually have some condition at play that applies to each element and that multiple elements can satisfy. We'll call that a predicate.

List<T> exposes a RemoveAll method that allows you to supply a predicate. Any item that matches that predicate is then removed. For example

Func<int, bool> isEven = i => i % 2 == 0;
List<int> ints = ...
ints.RemoveAll(item => isEven(item));
// ints will only contain odd numbers 

Other approaches to consider would be walking over the list backwards in a for loop and removing by index, building a second list containing the items to delete, and then in a second loop over the second list, remove items from the first. Or you could just write a query to construct a new sequence containing the items that you wish to keep.

Upvotes: 7

L.B
L.B

Reputation: 116108

change your foreach as below

foreach (int i in new List<int>(foo))

Upvotes: 1

Diego
Diego

Reputation: 18349

I think the best way is to iterate backwards using a simple for loop.

for(int i = foo.Count-1; i>=0; i--)
    if(foo[i]==2) foo.RemoveAt(i);

Upvotes: 2

Guffa
Guffa

Reputation: 700222

You can add the items that you want to remove to a temporary list, then remove them after the loop:

List<int> foo = new List<int>();
foo.Add(1);
foo.Add(2);
foo.Add(3);

List<int> remove = new List<int>();

foreach (int i in foo) {
  if (i==2) {
    remove.Add(i);
  }
}

foreach (int i in remove) {
  foo.Remove(i);
}

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726489

If you must remove entries while enumerating, walk the list in backward direction, and remove items that you need to remove.

for (var i = foo.Count-1 ; i >= 0 ; i--) {
    if (MustBeRemoved(foo[i])) {
        foo.RemoveAt(i);
    }
}

Note that this is not required in case of your post, where you know the values that need to be removed.

Upvotes: 12

Related Questions