How to Modify or Delete Items from an Enumerable Collection While Iterating Through It in C#

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

You can remove elements from a collection if you use a simple for loop.

Take a look at this example:

        var l = new List<int>();

l.Add(0);
l.Add(1);
l.Add(2);
l.Add(3);
l.Add(4);
l.Add(5);
l.Add(6);

for (int i = 0; i < l.Count; i++)
{
if (l[i] % 2 == 0)
{
l.RemoveAt(i);
i--;
}
}

foreach (var i in l)
{
Console.WriteLine(i);
}

Removing items from collection while iterating IEnumerable

No, that will either bomb or get you a result that still has bad elements. Just invert the Where expression:

var filtered = dataFromDataFeed.Where(bar => bar.Key >= fromTicks && bar.Key <= toTicks);
dataFromFeed = filtered.ToList(); // optional

It isn't clear whether you actually need the list to be updated, often it is not necessary since you have a perfectly good enumerator, so the last statement is // optional.

Keep in mind that using Remove() like you did in your original code has O(n*m) complexity, very bad. Using ToList() is only O(m) but requires O(m) storage. Trading speed for memory is a common programmer's decision but this is a slamdunk unless m is huge (hundreds of millions and you're fighting OOM) or very small. Neither should apply, given the expression.

How to remove objects from an Enumerable collection in a loop

There's a useful List<T>.RemoveAll(Predicate<T> match) method which I think is designed for this: http://msdn.microsoft.com/en-us/library/wdka673a.aspx

How to remove elements from a generic list while iterating over it?

Iterate your list in reverse with a for loop:

for (int i = safePendingList.Count - 1; i >= 0; i--)
{
// some code
// safePendingList.RemoveAt(i);
}

Example:

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i] > 5)
list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

Alternately, you can use the RemoveAll method with a predicate to test against:

safePendingList.RemoveAll(item => item.Value == someValue);

Here's a simplified example to demonstrate:

var list = new List<int>(Enumerable.Range(1, 10));
Console.WriteLine("Before:");
list.ForEach(i => Console.WriteLine(i));
list.RemoveAll(i => i > 5);
Console.WriteLine("After:");
list.ForEach(i => Console.WriteLine(i));

How add or remove object while iterating Collection in C#

foreach is designed for iterating over a collection without modifing it.

To remove items from a collection while iterating over it use a for loop from the end to the start of it.

for(int i = gems.Count - 1; i >=0 ; i--)
{
gems[i].Value.Update(gameTime);

if (gems[i].Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
Gem gem = gems[i];
gems.RemoveAt(i); // Assuming it's a List<Gem>
OnGemCollected(gem.Value, Player);
}
}

If it's a dictionary<string, Gem> for example, you could iterate like this:

foreach(string s in gems.Keys.ToList())
{
if(gems[s].BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(s);
}
}

One item gets deleted while iterating an IEnumerableT

Check out the documentation on Enumerable.Where. Specifically, the Remarks.

This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.

Which means that when you call Where you're not necessarily getting back an object such as a List or Array that just has X number of items in it. You're getting back an object that knows how to filter the IEnumerable<T> you called Where on, based on the predicate you provided. When you iterate that object, such as with a foreach loop or a call to Enumerable.Count() each item in the source IEnumerable<T> is evaluated against the predicate you provided and only the items that satisfy that predicate are returned.

Since the predicate you're providing checks the Sequence property, and you're modifying that property inside the first foreach loop, the second time you iterate entitiesToUpdateSequence fewer items match the predicate you provided and so you get a lower count. If you were to increment Sequence instead of decrement it, you might end up with a higher count the second time you iterate entitiesToUpdateSequence.

C# List - Removing items while looping / iterating

If you need to remove elements then you must iterate backwards so you can remove elements from the end of the list:

var data=new List<string>(){"One","Two","Three"};
for(int i=data.Count - 1; i > -1; i--)
{
if(data[i]=="One")
{
data.RemoveAt(i);
}
}

However, there are more efficient ways to do this with LINQ (as indicated by the other answers).

Why I should not modify a collection when I am iterating on it

Why I should not modify a collection when I am iterating on it?

Some collections can be modified when iterating, so it's not globally bad. In most cases it's very difficult to write an effective iterator that will work correctly even when the underlying collection is modified. In many cases the exception is the iterator writer punting and saying that they just don't want to deal with it.

In certain cases it's not clear what the iterator should do when the underlying collection is changed. Some cases are clear-cut, but for others different people will expect different behavior. Whenever you're in that situation it's a sign that there is a deeper problem (that you shouldn't mutating a sequence that you're iterating)

It is possible to create a collection that support to be modified when iterating on it, without to have any other problems ? (NOTE: that the first answer can answer this one too)

Sure.

Consider this iterator for a list:

public static IEnumerable<T> IterateWhileMutating<T>(this IList<T> list)
{
for (int i = 0; i < list.Count; i++)
{
yield return list[i];
}
}

If you remove an item at or before the current index from the underlying list then an item will be skipped while iterating. If you add an item at or before the current index the an item will be duplicated. But if you add/remove items past the current index during iteration then there won't be a problem. We could try to be fancy and make an attempt to see if an item was removed/added from the list and adjust the index accordingly, but it couldn't always work, so we wouldn't be able to handle all cases. If we had something like an ObservableCollection then we could be notified of additions/removals and their indexes and adjust the index accordingly, thus allowing the iterator to handle mutatings of the underlying collection (as long as it's not in another thread).

Since an iterator for an ObservableCollection can know both when any items are added/removed, and where they are, it can adjust it's position accordingly. I am unsure if the built in iterator properly handles mutation, but here is one that will handle any mutation of the underlying collection:

public static IEnumerable<T> IterateWhileMutating<T>(
this ObservableCollection<T> list)
{
int i = 0;
NotifyCollectionChangedEventHandler handler = (_, args) =>
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
if (args.NewStartingIndex <= i)
i++;
break;
case NotifyCollectionChangedAction.Move:
if (args.NewStartingIndex <= i)
i++;
if (args.OldStartingIndex <= i) //note *not* else if
i--;
break;
case NotifyCollectionChangedAction.Remove:
if (args.OldStartingIndex <= i)
i--;
break;
case NotifyCollectionChangedAction.Reset:
i = int.MaxValue;//end the sequence
break;
default:
//do nothing
break;
}
};
try
{
list.CollectionChanged += handler;
for (i = 0; i < list.Count; i++)
{
yield return list[i];
}
}
finally
{
list.CollectionChanged -= handler;
}
}
  • If an item is removed from "earlier" in the sequence, we continue normally without skipping an item.

  • If an item is added "earlier" in the sequence we won't show it, but we also won't show some other item twice.

  • If an item is moved from before the current position to after it will be shown twice, but no other item will be skipped or repeated. If an item is moved from after the current position to before the current position it won't be shown, but that's all. If an item is moved from either later in the collection to another spot later, there is no problem, and the move will be seen in the result, if it is moved from an earlier spot to another earlier spot, everything is fine and the move won't be "seen" by the iterator.

  • Replacing an item isn't a problem; it will only be seen if it's "after" the current position though.

  • Resetting the collection results in the sequence ending gracefully at the current position.

Note that this iterator won't handle situations with multiple threads. If another thread mutates the collection while another is iterating, bad things could happen (items being skipped or repeated, or even exceptions, such as index out of bounds exceptions). What this does allow is mutations during iteration in which there is either only one thread, or in which only one thread is ever executing code that moves the iterator or mutates the collection.

When the C# compiler generate the Enumerator interface implementation takes into account this such of things?

The compiler doesn't generate the interface implementation; a person does.



Related Topics



Leave a reply



Submit