C# Foreach Loop Doesn't Exit Properly [duplicate] - c#

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.

Related

Looping Observable Collection from the Last Item

foreach(var item in myObservalbleCollection)
{
//samoe coding
}
Above code will loop the items in the observable class from the beginning. I want to loop the this object from the last Item
ex 9,8,7,6,5,4,3,2,1,0 instead of 1,2,3,4,5,6,7,8,9...
Please give me a solution to do this in c#
Instead of using foreach you can use a for loop running from the end of the collection and to the start:
for (var i = myObservableCollection.Count - 1; i >= 0; i -= 1) {
var item = myObservableCollection[i];
// Process item
}
This is possible because ObservableCollection<T> implement IList<T> which provides both the count of the elements in the collection and indexed access to these elements in addition to the forward teration provided by IEnumerable<T> used by foreach. This is the most efficient solution.
You can also use LINQ Reverse which is available for any collection implementing IEnumerable<T>:
foreach (var item in myObservableCollection.Reverse()) {
// Process item
}
This will actually copy all the elements in the collection to a new array before iterating them in reverse order. While less efficient than the first solution it should not matter in most cases.
You could reverse the observablecollection and do the loop
var myObservalbleCollection = new ObservableCollection<YourType>(collection.Reverse());
foreach(var item in myObservalbleCollection)
{
//some coding
}

Fast ways to avoid duplicates in a List<> in C#

My C# program generates random strings from a given pattern. These strings are stored in a list. As no duplicates are allowed I'm doing it like this:
List<string> myList = new List<string>();
for (int i = 0; i < total; i++) {
string random_string = GetRandomString(pattern);
if (!myList.Contains(random_string)) myList.Add(random_string);
}
As you can imagine this works fine for several hundreds of entries. But I'm facing the situation to generate several million strings. And with each added string checking for duplicates gets slower and slower.
Are there any faster ways to avoid duplicates?
Use a data structure that can much more efficiently determine if an item exists, namely a HashSet. It can determine if an item is in the set in constant time, regardless of the number of items in the set.
If you really need the items in a List instead, or you need the items in the resulting list to be in the order they were generated, then you can store the data in both a list and a hashset; adding the item to both collections if it doesn't currently exist in the HashSet.
The easiest way is to use this:
myList = myList.Distinct().ToList();
Although this would require creating the list once, then creating a new list. A better way might be to make your generator ahead of time:
public IEnumerable<string> GetRandomStrings(int total, string pattern)
{
for (int i = 0; i < total; i++)
{
yield return GetRandomString(pattern);
}
}
...
myList = GetRandomStrings(total, pattern).Distinct().ToList();
Of course, if you don't need to access items by index, you could probably improve efficiency even more by dropping the ToList and just using an IEnumerable.
Don't use List<>. Use Dictionary<> or HashSet<> instead!
You could use a HashSet<string> if order is not important:
HashSet<string> myHashSet = new HashSet<string>();
for (int i = 0; i < total; i++)
{
string random_string = GetRandomString(pattern);
myHashSet.Add(random_string);
}
The HashSet class provides high-performance set operations. A set is a collection that contains no duplicate elements, and whose elements are in no particular order.
MSDN
Or if the order is important, I'd recommend using a SortedSet (.net 4.5 only)
not a good way but kind of quick fix,
take a bool to check if in whole list there is any duplicate entry.
bool containsKey;
string newKey;
public void addKey(string newKey){
foreach(string key in MyKeys){
if(key == newKey){
containsKey = true;
}
}
if(!containsKey){
MyKeys.add(newKey);
}else{
containsKey = false;
}
}
A Hashtable would be a faster way to check if an item exists than a list.
Have you tried:
myList = myList.Distinct()

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.

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]);
}
}

Problems removing elements from a list when iterating through the list

I have a loop that iterates through elements in a list. I am required to remove elements from this list within the loop based on certain conditions. When I try to do this in C#, I get an exception. apparently, it is not allowed to remove elements from the list which is being iterated through. The problem was observed with a foreach loop. Is there any standard way to get around this problem?
Note : One solution I could think of is to create a copy of the list solely for iteration purpose and to remove elements from the original list within the loop. I am looking for a better way of dealing with this.
When using List<T> the ToArray() method helps in this scenario vastly:
List<MyClass> items = new List<MyClass>();
foreach (MyClass item in items.ToArray())
{
if (/* condition */) items.Remove(item);
}
The alternative is to use a for loop instead of a foreach, but then you have to decrement the index variable whenever you remove an element i.e.
List<MyClass> items = new List<MyClass>();
for (int i = 0; i < items.Count; i++)
{
if (/* condition */)
{
items.RemoveAt(i);
i--;
}
}
If your list is an actual List<T> then you can use the built-in RemoveAll method to delete items based on a predicate:
int numberOfItemsRemoved = yourList.RemoveAll(x => ShouldThisItemBeDeleted(x));
You could use LINQ to replace the initial list by a new list by filtering out items:
IEnumerable<Foo> initialList = FetchList();
initialList = initialList.Where(x => SomeFilteringConditionOnElement(x));
// Now initialList will be filtered according to the condition
// The filtered elements will be subject to garbage collection
This way you don't have to worry about loops.
You can use integer indexing to remove items:
List<int> xs = new List<int> { 1, 2, 3, 4 };
for (int i = 0; i < xs.Count; ++i)
{
// Remove even numbers.
if (xs[i] % 2 == 0)
{
xs.RemoveAt(i);
--i;
}
}
This can be weird to read and tough to maintain, though, especially if the logic in the loop gets any more complex.
Another trick is to loop through the list backwards.. removing an item won't affect any of the items you are going to encounter in the rest of the loop.
I'm not recommending this or anything else though. Everything you need this for can probably be done using LINQ statements to filter the list on your requirements.
You can iterate with foreach this way:
List<Customer> custList = Customer.Populate();
foreach (var cust in custList.ToList())
{
custList.Remove(cust);
}
Note: ToList on the list of variables, this iterates through the list created by the ToList but removes the items from the original list.
Hope this helps.
The recommended solution is to put all your elements you want to remove in a separate list and after the first loop, put a second loop where you iterate over the remove-list and remove those elements form the first list.
The reason you get an error is because you're using a foreach loop. If you think about how a foreach loop works this makes sense. The foreach loop calls the GetEnumerator method on the List. If you where to change the number of elements in the List, the Enumerator the foreach loop holds wouldn't have the correct number of elements. If you removed an element a null exception error would be thrown, and if you added an element the loop would miss an item.
If you like Linq and Lamda expressions I would recommend Darin Dimitrov solution, otherwise I would use the solution provided by Chris Schmich.

Categories