I am trying to remove object while I am iterating through Collection. But I am getting exception. How can I achieve this?
Here is my code :
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(gem.Key); // I can't do this here, then How can I do?
OnGemCollected(gem.Value, Player);
}
}
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);
}
}
The easiest way is to do what #IV4 suggested:
foreach (var gem in gems.ToList())
The ToList() will convert the Dictionary to a list of KeyValuePair, so it will work fine.
The only time you wouldn't want to do it that way is if you have a big dictionary from which you are only removing relatively few items and you want to reduce memory use.
Only in that case would you want to use one of the following approaches:
Make a list of the keys as you find them, then have a separate loop to remove the items:
List<KeyType> keysToRemove = new List<KeyType>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
OnGemCollected(gem.Value, Player);
keysToRemove.Add(gem.Key);
}
}
foreach (var key in keysToRemove)
gems.Remove(key);
(Where KeyType is the type of key you're using. Substitute the correct type!)
Alternatively, if it is important that the gem is removed before calling OnGemCollected(), then (with key type TKey and value type TValue) do it like this:
var itemsToRemove = new List<KeyValuePair<TKey, TValue>>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
itemsToRemove.Add(gem);
}
foreach (var item in itemsToRemove)
{
gems.Remove(item.Key);
OnGemCollected(item.Value, Player);
}
As the other answers say, a foreach is designed purely for iterating over a collection without modifying it as per the documenation:
The foreach statement is used to iterate through the collection to get
the desired information, but should not be used to change the contents
of the collection to avoid unpredictable side effects.
in order to do this you would need to use a for loop (storing the items of the collection you need to remove) and remove them from the collection afterwards.
However if you are using a List<T> you could do this:
lines.RemoveAll(line => line.FullfilsCertainConditions());
After going through all the answers, and being equally good. I faced a challenge where I had to modify a List and what I ended up doing worked quite well for me. So just in case anyone finds it useful. Can someone provide me feedback on how efficient it might be.
Action removeFromList;
foreach(var value in listOfValues){
if(whatever condition to remove is){
removeFromList+=()=>listOfValues.remove(value);
}
}
removeFromList?.Invoke();
removeFromList = null;
You should use the for loop instead of the foreach loop. Please refer here
Collections support foreach statement using Enumarator. Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext or Reset throws an InvalidOperationException.
Use for loop for collection modifying.
Related
For now, the best I could think of is:
bool oneMoreTime = true;
while (oneMoreTime)
{
ItemType toDelete=null;
oneMoreTime=false;
foreach (ItemType item in collection)
{
if (ShouldBeDeleted(item))
{
toDelete=item;
break;
}
}
if (toDelete!=null)
{
collection.Remove(toDelete);
oneMoreTime=true;
}
}
I know that I have at least one extra variable here, but I included it to improve the readability of the algorithm.
The "RemoveAll" method is best.
Another common technique is:
var itemsToBeDeleted = collection.Where(i=>ShouldBeDeleted(i)).ToList();
foreach(var itemToBeDeleted in itemsToBeDeleted)
collection.Remove(itemToBeDeleted);
Another common technique is to use a "for" loop, but make sure you go backwards:
for (int i = collection.Count - 1; i >= 0; --i)
if (ShouldBeDeleted(collection[i]))
collection.RemoveAt(i);
Another common technique is to add the items that are not being removed to a new collection:
var newCollection = new List<whatever>();
foreach(var item in collection.Where(i=>!ShouldBeDeleted(i))
newCollection.Add(item);
And now you have two collections. A technique I particularly like if you want to end up with two collections is to use immutable data structures. With an immutable data structure, "removing" an item does not change the data structure; it gives you back a new data structure (that re-uses bits from the old one, if possible) that does not have the item you removed. With immutable data structures you are not modifying the thing you're iterating over, so there's no problem:
var newCollection = oldCollection;
foreach(var item in oldCollection.Where(i=>ShouldBeDeleted(i))
newCollection = newCollection.Remove(item);
or
var newCollection = ImmutableCollection<whatever>.Empty;
foreach(var item in oldCollection.Where(i=>!ShouldBeDeleted(i))
newCollection = newCollection.Add(item);
And when you're done, you have two collections. The new one has the items removed, the old one is the same as it ever was.
Just as I finished typing I remembered that there is lambda-way to do it.
collection.RemoveAll(i=>ShouldBeDeleted(i));
Better way?
A forward variation on the backward for loop:
for (int i = 0; i < collection.Count; )
if (ShouldBeDeleted(collection[i]))
collection.RemoveAt(i)
else
i++;
You cannot delete from a collection inside a foreach loop (unless it is a very special collection having a special enumerator). The BCL collections will throw exceptions if the collection is modified while it is being enumerated.
You could use a for loop to delete individual elements and adjust the index accordingly. However, doing that can be error prone. Depending on the implementation of the underlying collection it may also be expensive to delete individual elements. For instance deleting the first element of a List<T> will copy all the remaning elements in the list.
The best solution is often to create a new collection based on the old:
var newCollection = collection.Where(item => !ShouldBeDeleted(item)).ToList();
Use ToList() or ToArray() to create the new collection or initialize your specific collection type from the IEnumerable returned by the Where() clause.
The lambda way is good. You could also use a regular for loop, you can iterate lists that a for loop uses within the loop itself, unlike a foreach loop.
for (int i = collection.Count-1; i >= 0; i--)
{
if(ShouldBeDeleted(collection[i])
collection.RemoveAt(i);
}
I am assuming that collection is an arraylist here, the code might be a bit different if you are using a different data structure.
In my example code below i would like to replace the item in the dictionary with a new item or assign new values to that item. How can i do this?
This is my code:
dynamic data = jss.Deserialize<dynamic>(jsonText);
foreach (KeyValuePair<string,object> item in data["typeData"])
{
if(item.Key == "somevalue")
{
item = new KeyValuePair<string,object>();
}
}
I'm getting:
Cannot assign to 'item' because it is a 'foreach iteration variable'
There must be a work-around.
foreach are considered to be read only contexts.
Don't use foreach as the message says, convert the loop to a regular for loop.
From MSDN:
This error occurs when an assignment to variable occurs in a read-
only context. Read-only contexts include foreach iteration variables,
using variables, and fixed variables. To resolve this error, avoid
assignments to a statement variable in using blocks, foreach
statements, and fixed statements.
In your case the object item is not a reference its simply a copy hence any change you make to it will not result in a change in the original object.
Depends on what you want. Do you just need to override the value? I assume so because replacing the key and value would be a very different operation (remove one item and insert another)
just iterate over the keys instead of the collection (Assuming it's a dictionary):
dynamic data = jss.Deserialize<dynamic>(jsonText)["typeData"];
foreach (string key in data.Keys)
{
if(key == "somevalue")
{
data[key] = ...;
}
}
if there's no keys property you can substitute that part with (assuming that at least there's an indexer)
foreach (string key in data.Select(pair=>pair.Key)){
...
}
The problem with your code is that you are attempting to change the value of a variable that is used as a placeholder. The variable "item" simply has the same reference that exists in the dictionary; changing the object that "item" references won't actually change anything in the Dictionary itself, and on top of that it can screw up the logic of looping through the Dictionary.
In addition to not being able to reassign the placeholder, you are not allowed to add or remove items from the Dictionary within a foreach loop that uses said Dictionary, because that will also mess up the logic of iterating through the Dictionary's items (the item that is the "current" item of the enumerator behind the scenes now no longer exists, so the enumerator may lose its place in the collection and not be able to continue.
The workaround is to enumerate a different collection when you change the original collection. Basically, a task like this requires two passes; first collect the items you want to change, then enumerate through THAT collection and make the change to the original collection:
...
var itemsToChange = new List<KeyValuePair<string, object>>();
foreach (var item in data["typeData"])
{
if(item.Key == "somevalue")
itemsToChange.Add(item);
}
foreach(var item in itemsToChange)
{
//even here you can't just "swap out" KVPs;
//you must remove the old and add the new
data["typeData"].Remove(item);
data["typeData"].Add(someNewString, someNewObject);
}
You have to either use a for loop or store the variables you want changed and change them outside of the foreach loop.
Perhaps there's something missing from your question, but it seems that the workaround is to avoid looping entirely:
dynamic data = jss.Deserialize<dynamic>(jsonText);
var item = new KeyValuePair<string, object>("somevalue", data["somevalue"]);
or perhaps:
dynamic data = jss.Deserialize<dynamic>(jsonText);
DoSomethingWith(data["somevalue"]);
what's the reason for your loop?
Look at this csharp code, and see if you can tell why I need to exit the loop after I found and deleted an item from the list. The idea is to go through a node's list of neighbors, and see if a Node n exists there, then delete that neighbor:
internal void RemoveDirected(Node n)
{
foreach (EdgeToNeighbor etn in this.Neighbors)
{
if (etn.Neighbor.Key == n.Key)
{
RemoveDirected(etn);
break;
}
}
}
internal void RemoveDirected(EdgeToNeighbor e)
{
Neighbors.Remove(e);
}
.
.
.
// Removes EdgeToNeighbor instance from AdjacencyList
protected internal virtual void Remove(EdgeToNeighbor e)
{
base.InnerList.Remove(e);
}
Notice how I have a "break" after the RemoveDirected call in the first method.
I've found that if I didn't exit after the RemoveDirected
it would go on forever in the foreach loop. I suppose it must have
something to do with the way foreach works. If you modify the list
that foreach is working on, it gets confuse and loops forever.
Have you seen this type of thing, and what are other options to use rather than using break?
Of course, I could place the node that I've found in a local variable, then break from the loop, and
delete it outside of the loop. But I was thinking, may be there are better ways to do this
in csharp.
When you iterate a .NET collection using its iterator, you must not modify that collection. If you do, you are asking for trouble.
You should defer the deletion instead of deleting right in the foreach loop. For example, you can collect everything you need to delete in a list, and then delete it outside of foreach.
var toDelete = this.Neighbors.Where(etn => etn.Neighbor.Key == n.Key).ToList();
foreach (var e in toDelete) {
Neighbors.Remove(e);
}
You definitely don't want to use an iterator if you are removing items. One option is to change it to downward counting loop:
for (int nI = this.Neighbors.Count; nI >= 0; nI--)
{
var etn = this.Neighbors[nI];
if (etn.Neighbor.Key == n.Key)
{
RemoveDirected(n);
}
}
You could also collect the keys or items to be deleted in a collection within the loop and then perform the deletion once you have completed your iteration.
However, if you are only removing a single item and the items in the collection are keyed somehow, then you shouldn't actually need any loop. Just test the existence of the key or item in the collection and, if it is there, just remove it.
You could get the number of nodes and then check and delete from highest to lowest in a for loop that way you avoid looking for an item that no longer exists.
I have a construction similar to this (but a lot more complicated):
var list = new List<string>();
// .. populate list ..
foreach(var item in list)
{
DoFunction(list);
}
public void DoFunction(List<string> list)
{
if(someCondition == true)
{
// .. modify list in here ..
}
}
Now, I understand that its not possible to edit the collection you're foreaching through, but how do you jump out of the loop gracefully if you do have to edit the list (without a try catch statement)? Is there some way to tell if the list has been edited? Can you edit the list and quickly break; before it notices?
Yes, you could break, if that's what you really want. An exception won't be thrown until the for loop tries to grab the next item from the list.
But I've found it's easiest just to create and iterate across a copy of the list so you don't have to worry about it.
foreach(var item in list.ToList())
The added performance overhead of an extra, untouched list is generally negligible compared to the maintainability costs of more complex code.
Rather than use a foreach construct, a for loop would allow you to alter the list.
for (var x = 0; x < list.Count; x++) {
}
It's hard to offer useful advice without knowing what kinds of edits are being made. The pattern that I've found is has the most general-purpose value, though, to just construct a new list.
For example, if you need to look at each item and decide between removing it, leaving it as-is, or inserting items after it, you could use a pattern like this:
IEnumerable<string> butcherTheList(IEnumerable<string> input)
{
foreach (string current in input)
{
if(case1(current))
{
yield return current;
}
else if(case2(current))
{
yield return current;
yield return someFunc(current);
}
// default behavior is to yield nothing, effectively removing the item
}
}
List<string> newList = butcherTheList(input).ToList();
My windows phone 7 silverlight app before placing pushpins on a map layer removes any that are previously there.
I was doing this in a foreach loop as follows:
//Clear previous pins
try
{
foreach (UIElement p in PushPinLayer.Children)
{
if(p.GetType() == typeof(Pushpin))
{
PushPinLayer.Children.Remove(p);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
//TODO: For some reason the foreach loop above causes an invalid Operation exception.
//Cathing the error here until I can work out why it is happening.
}
This code removes any pushpins as required but after the last loop would throw an exception "Invalid Operation" I re-wrote it as a for loop:
for (int i = 0; i < PushPinLayer.Children.Count; i++)
{
if (PushPinLayer.Children[i].GetType() == typeof(Pushpin))
{
PushPinLayer.Children.RemoveAt(i);
}
}
Which works fine, however I can't see why the foreach is throwing an error.
This is very normal,
You cannot remove items from a list which you still use in the foreach list.
better then removing the item would be to create a new list, and every time it is not a pushpin type, add the object to the new list.
This way the original list is not altered and you won't get an exception.
I find it strange that the for loop works, but if it does, it would mean that the way they are itterated is different. The for loop will be copied to another memory location and used for the for loop so that the original one, in which you remove items, is not used anymore by the for loop. The foreach loop will get parameters from the list, the you remove items, so the list and the parameters become concurrent.
Your foreach loop uses and Enumerator to iterate the objects in your collection. When you delete an object from the collection the Enumerator is no longer valid as it references object that no longer exist. This causes a InvalidOperationException
The best way to remove them is using a for loop and even better doing it in reverse.
for (int i = PushPinLayer.Children.Count - 1; i >= 0 ; i--)
{
if (PushPinLayer.Children[i].GetType() == typeof(Pushpin))
{
PushPinLayer.Children.RemoveAt(i);
}
}
This will ensure that as the items are removed your Index i does not exceed the number of items in your collection.
Since others have already answered your question, I will only comment on your usage of xxx.GetType() == typeof(Pushpin). You could check whether something is a pushpin by simply using the C# reserved keyword is. e.g.:
if (p is Pushpin) {...}
When you change the content of the collection, the enumerator used in the foreach loop becomes invalid. You can't change a collection while you enumerate it.
Here's a workaround:
List<UIElement> toRemove = new List<UIElement>();
foreach (UIElement p in PushPinLayer.Children)
{
if(p.GetType() == typeof(Pushpin))
{
toRemove.Add(p);
}
}
foreach(UIElement p in toRemove)
{
PushPinLayer.Children.Remove(p);
}
Alternatively, you could use the RemoveAll method, which takes a predicate as a parameter:
PushPinLayer.Children.RemoveAll(p => p is Pushpin);
Others have already given you the reason for the problem so I just thought I'd post a LINQ version of a work around
var toRemove = PushPinLayer.Children.OfType<Pushpin>().ToList();
// since toRemove is a separate collection, it's safe to do this now:
foreach (var child in toRemove)
PushPinLayer.Children.Remove(child)
You must not change the collection while foreaching through it.