Modifying list inside foreach loop - c#

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();

Related

How to edit an iterator within a foreach loop

Scenario
I have a system that holds races, each race has a unique list of members on that race. (the list is a List< T > )
I want users to be able to remove a member (if they ARE this member) from the list of members on that race.
Problem
I'm trying to get the following code to work:
foreach (string item in hillracing.searchRaces(RaceID).RaceList) // Loop through List with foreach.
{
if (item == SelectedItem)
{
item = null;
}
}
I can't edit the variable because it is in a foreach loop, how would I achieve this another way?
You can just store it and remove it form the collection afterwards.
var toRemove = null;
foreach (string item in hillracing.searchRaces(RaceID).RaceList) // Loop through List with foreach.
{
if (item == SelectedItem)
{
toRemove = item;
break; //Can break here if you're sure there's only one SelectedItem
}
}
hillracing.searchRaces(RaceID).Racelist.Remove(toRemove);
though in this case you could also just use hillracing.searchRaces(RaceID).Racelist.Remove(SelectedItem); and you won't use the foreach loop at all.
You can't modify collection that you are looping using foreach loop. The collection used in foreach is immutable. This is by design.
The foreach statement is used to iterate through the collection to get
the information that you want, but can not be used to add or remove
items from the source collection to avoid unpredictable side effects.
If you need to add or remove items from the source collection, use a
for loop.
Using Linq you shouldn't need to loop to find the entry you want to nullify...
// Use of Single() here assumes the object definitely exists.
// Use SingleOrDefaul() if there is a chance it might not exist.
var item = hillracing.searchRaces(RaceID)
.RaceList
.Where(x => x.Item == SelectedItem).Single();
item = null;
Edit: Since you've changed the requirement to remove the item from the list, I think you'd just call the Remove method`with the found item. So the code becomes
// Use of Single() here assumes the object definitely exists.
// Use SingleOrDefaul() if there is a chance it might not exist.
var item = hillracing.searchRaces(RaceID)
.RaceList
.Where(x => x.Item == SelectedItem).Single();
hillracing.searchRaces(RaceID).RaceList.Remove(item);
You can't do that in a foreach loop. If it's an IList/IList<T> which allows random access, like an array or list, you can use a for-loop:
List<string> = hillracing.searchRaces(RaceID).RaceList;
for(int i = 0; i < list.Count; i++)
{
if(list[i] == SelectedItem)
list[i] = null;
}
So you can't add or remove items in the foreach but you also can't replace the reference. The object refers to the original value so you could modify the object(if strings werent immutable) but you can't replace the reference itself in a foreach. This is related.
Use the existing Remove()-method to search and remove the item for you:
hillracing.searchRaces(RaceID).RaceList.Remove(SelectedItem);

How add or remove object while iterating Collection in C#

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.

Strange behaviour between foreach and for loop

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.

How to remove elements from an array

Hi I'm working on some legacy code that goes something along the lines of
for(int i = results.Count-1; i >= 0; i--)
{
if(someCondition)
{
results.Remove(results[i]);
}
}
To me it seems like bad practice to be removing the elements while still iterating through the loop because you'll be modifying the indexes.
Is this a correct assumption?
Is there a better way of doing this? I would like to use LINQ but I'm in 2.0 Framework
The removal is actually ok since you are going downwards to zero, only the indexes that you already passed will be modified. This code actually would break for another reason: It starts with results.Count, but should start at results.Count -1 since array indexes start at 0.
for(int i = results.Count-1; i >= 0; i--)
{
if(someCondition)
{
results.RemoveAt(i);
}
}
Edit:
As was pointed out - you actually must be dealing with a List of some sort in your pseudo-code. In this case they are conceptually the same (since Lists use an Array internally) but if you use an array you have a Length property (instead of a Count property) and you can not add or remove items.
Using a list the solution above is certainly concise but might not be easy to understand for someone that has to maintain the code (i.e. especially iterating through the list backwards) - an alternative solution could be to first identify the items to remove, then in a second pass removing those items.
Just substitute MyType with the actual type you are dealing with:
List<MyType> removeItems = new List<MyType>();
foreach(MyType item in results)
{
if(someCondition)
{
removeItems.Add(item);
}
}
foreach (MyType item in removeItems)
results.Remove(item);
It doesn't seem like the Remove should work at all. The IList implementation should fail if we're dealing with a fixed-size array, see here.
That being said, if you're dealing with a resizable list (e.g. List<T>), why call Remove instead of RemoveAt? Since you're already navigating the indices in reverse, you don't need to "re-find" the item.
May I suggest a somewhat more functional alternative to your current code:
Instead of modifying the existing array one item at a time, you could derive a new one from it and then replace the whole array as an "atomic" operation once you're done:
The easy way (no LINQ, but very similar):
Predicate<T> filter = delegate(T item) { return !someCondition; };
results = Array.FindAll(results, filter);
// with LINQ, you'd have written: results = results.Where(filter);
where T is the type of the items in your results array.
A somewhat more explicit alternative:
var newResults = new List<T>();
foreach (T item in results)
{
if (!someCondition)
{
newResults.Add(item);
}
}
results = newResults.ToArray();
Usually you wouldn't remove elements as such, you would create a new array from the old without the unwanted elements.
If you do go the route of removing elements from an array/list your loop should count down rather than up. (as yours does)
a couple of options:
List<int> indexesToRemove = new List<int>();
for(int i = results.Count; i >= 0; i--)
{
if(someCondition)
{
//results.Remove(results[i]);
indexesToRemove.Add(i);
}
}
foreach(int i in indexesToRemove) {
results.Remove(results[i]);
}
or alternatively, you could make a copy of the existing list, and instead remove from the original list.
//temp is a copy of results
for(int i = temp.Count-1; i >= 0; i--)
{
if(someCondition)
{
results.Remove(results[i]);
}
}

Foreach statement in listbox

I have a problem with a foreach statement in my project. So far I have the code:
foreach(object i in listboxFiles.Items)
{
if (i == ".ftpquota")
{
listboxFiles.Items.Remove(i);
}
if (i == ".")
{
listboxFiles.Items.Remove(i);
}
if (i == "..")
{
listboxFiles.Items.Remove(i);
}
}
I have this in a 1 second timer. It gets the item name all right, but when it gets to the if statements it says that they do not match, but they do?
First thing, you are changing a collection while iterating over it. This cannot work, so your code is fundamentally broken.
There are several ways to fix this; the simplest in your case would be to copy the items collection, iterating over the copy and changing (= removing from) the original:
var items = new System.Collections.ArrayList(listboxFiles.Items);
foreach (var item in items) {
if (item.Equals("."))
listboxFiles.Items.remove(item);
…
}
Secondly, you are comparing an object to a string, hence the == operator does reference equality checking rather than testing for string equality. Either use Equals or do an appropriate cast.
The equality check is not working because you should cast to string first and do an appropriate string comparison.
e.g.
if (string.Equals((string)i, ".ftpquota", StringComparison.Ordinal))
If you remove items from a collection of items whilst iterating through the collection, you may well run into trouble. One way to get around this problem is to start with the last item and count backwards, thus any removals you do will not affect the remaining items of the collection, e.g.
for(var i = listboxFiles.Items.Count - 1; i >= 0; --i)
{
var item = listboxFiles[i];
if (...)
{
listboxFiles.Items.RemoveAt(i);
}
}

Categories