0

Possible Duplicate:
How to modify or delete items from an enumerable collection while iterating through it in C#

Listen, I do not want to know about the basic foreach. I am talking about that one which control this error:

"The enumerator is not valid because the collection changed."

Which occur when I do this:

foreach(Image image in images)
{
   if(...)
   {
       images.remove(image)
   }
}

I believe there is an special iterator which handle this well, as Java has. So, how can I do this in C# please? Thank you!

Community
  • 1
  • 1
alansiqueira27
  • 8,129
  • 15
  • 67
  • 111

4 Answers4

5

Or just remove it without manually iterating at all:

images.RemoveAll(image=>...)

Works on List<T> but many other containers don't support it.

An O(n) solution working on IList<T>:

void RemoveWhere(this IList<T> list,Predicate<T> pred)
{
    int targetIndex=0;
    for(int srcIndex=0;srcIndex<list.Count;srcIndex++)
    {
      if(pred(list[srcIndex]))
      {
        list[targetIndex]=list[srcIndex];
        targetIndex++;
      }
      for(int i=list.Count-1;i>=targetIndex;i--)
        list.RemoveAt(i);
    }
}

Can be sped up a bit by not assigning until you hit the first removed item.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • in my case, I will only remove IF a condition is true. – alansiqueira27 Dec 08 '10 at 21:48
  • The lamda is a condition. The name of the functions is misleading. I would have called it `RemoveWhere` – CodesInChaos Dec 08 '10 at 21:50
  • alas, RemoveAll is one of the LINQ extension methods, so naming it something else is pretty much not an option. I agree, though, that this method, along with some others, were poorly named. – Dr. Wily's Apprentice Dec 08 '10 at 22:01
  • Doh! disregard my previous comment. My brain must have been somewhere else; RemoveAll is not a LINQ method. I should have realized, since the LINQ extension methods don't perform manipulations. So anyway, I guess I'm just in agreement that the `RemoveAll` method is poorly named. – Dr. Wily's Apprentice Dec 08 '10 at 22:29
4
for (var i = 0; i < images.Count; ++i)
{
    if (...)
    {
        images.RemoveAt(i);
        --i;
    }
}
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • but If I do this, I believe I will lose the next object, considering I am using Canvas. For example: Image that I removed the object of the index 2. So the object of index 2 will move to index 2 as a List. So when I try to get the next value which is index 3, the old object of index 3 will be at index 2. So I will lose him. – alansiqueira27 Dec 08 '10 at 21:47
  • @Alan, that's the purpose of the line `--i;`. This adjusts the index so that no items are missed. – Dr. Wily's Apprentice Dec 08 '10 at 21:50
  • that's what the `--i` corrects. The technique of decrementing when removing is interesting. I usually wrote my loop to go backwards and double-test sometimes. – CodesInChaos Dec 08 '10 at 21:52
  • This is O(n^2) for most containers. It's possible to write O(n) time and O(1) space solutions on `IList` – CodesInChaos Dec 08 '10 at 21:54
  • It is also sometimes easier to loop backwards if you're removing items: for (int i = images.Count - 1; i <= 0; --i) – TheEvilPenguin Dec 08 '10 at 22:27
2

You can't do it in C#.

What you can do is collect the objects you want to remove, then remove them:

Image[] imagesToRemove = images.Where(image => ...).ToArray();
foreach (Image image in imagesToRemove)
    images.remove(image);
Tim Robinson
  • 53,480
  • 10
  • 121
  • 138
  • And I think your code is typically O(n^2) since `Remove` on most collections is O(n). On `HashSet` it would be fast, but HashSet already offers built in methods for this. – CodesInChaos Dec 08 '10 at 21:49
1

Kent's answer will work given something that implements IList<T>. For something that doesn't you will have to build up a list of the things you want to remove. For example:

public static void RemoveWhere<T>(this ICollection<T> self, Func<T, bool> predicate)
{
    var toRemove = self.Where(predicate).ToList();

    foreach (var i in toRemove)
        self.Remove(i);
}
cdhowie
  • 158,093
  • 24
  • 286
  • 300