How to remove all elements from a dictionary? - c#

I use the following code to remove all elements from a dictionary:
internal static void RemoveAllSourceFiles()
{
foreach (byte key in taggings.Keys)
{
taggings.Remove(key);
}
}
But unfortunately this isn't working because an InvalidOperationException is thrown. I know this is because the collection is modified while iterating over it, but how can I change that?

A much simpler (and much more efficient) approach:
taggings.Clear();
and yes, the error is because changing the data deliberately breaks iterators.

Try using the Clear method instead.
internal static void RemoveAllSourceFiles()
{
taggings.Clear();
}
Update: And as Marc pointed out, you cannot continue iterating over a collection while you modify it because the iterator is irrecoverably invalidated. Please read the answer to this SO question for details.
Why does enumerating through a collection throw an exception but looping through its items does not

Dictionary.Clear?

To do what you want to do you are going to need to iterate through the keys in reverse, that way you do not modify the array in the order it is trying to return to you.
Either that or use .Clear()

As said, the .NET default enumerator doesn't support collection changes while enumerating. In your case use Clear.
If you want better control over deletion, use linq:
var deletionList = (from tag in taggings where <where clause> select tag.Key).ToArray();
foreach(var key in deletionList)
{
taggings.Remove(key);
}
The ToArray() extension method will enumerate the LINQ query, and instantiate an array storing the results. This array can be safely enumerated later to delete the contained items in the source dictionary.

I know this is an old question, but for any who's looking for an answer, here some options:
To list() and then use the .remove property.
Make it null/nothing and reinit it?
Forloop in stead of foreach?
while Dict.Count()
clDictObject oBj = Dict.Keys.ElementAt(dict.Count -1);
Dict.Remove(oBj)
end while

Related

does foreach loop handle Changes in list length correctly?

does foreach correctly iterate over flexible list?
for example
//will iterate over all items in list?
foreach (var obj in list)
{
//list length changes here
//ex:
list.Add(...);
list.Remove(...);
list.Concat(...);
// and so on
}
and if it does ...how?
You can't modify a collection while enumerating it inside a foreach statement.
You should use another pattern to do what you are trying to do because the for each does not allow you to change the enumerator you are looping to.
For Example:
Imagine if you run a foreach on a sorted list from the beginning, you start processing item with key="A" then you go to "B" then you change "C" to "B", what's going to happen? Your list is resorted and you don't know anymore what you are looping and where you are.
In general you "could" do it with a for(int i=dictionary.count-1; i>=0; --i) or something like that but this also depends on your context, I would really try to use another approach.
Internal Working: IEnumerator<t> is designed to enable the iterator pattern for iterating over collections of elements, rather than the length-index. IEnumerator<t> includes two members.
The first is bool MoveNext(). Using this method, we can move from one element within the collection to the next while at the same time detecting when we have enumerated through every item using the Boolean return.
The second member, a read-only property called Current, returns the element currently in process. With these two members on the collection class, it is possible to iterate over the collection simply using a while loop.
The MoveNext() method in this listing returns false when it moves past the end of the collection. This replaces the need to count elements while looping. (The last member on IEnumerator<t> , Reset(), will reset the enumeration.)
Per the documentation, if changes are made inside the loop the behavior is undefined. Undefined means that there are no restrictions on what it can do, there is no "incorrect behavior" when the behavior is undefined...crash, do what you want, send an email to your boss calling him nasty names and quiting, all equally valid. I would hope for a crash in this case, but again, whatever happens, happens and is considered "correct" according to the documentation.
You cannot change the collection inside the for each loop of the same collection.
if you want you can use for loop to change the collection length.
The collection you use in a foreach loop is immutable. As per MSDN
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.
But as per this link, it looks like this is now possible from .Net 4.0

In c# is it safe to expand a List that's being traversed with foreach?

In c# is it safe to expand a List that's being traversed with foreach?
I assume that by expand you mean to add new items to the collection. If so then the answer is not, you will get an exception on the traversal. I don't believe any collection can do this.
You can create a new list and then do an AddRange on the original list.
In c# is it safe to expand a List that's being traversed with foreach? If not then how about other collections?
There are very few collections that safely let you add to them while being iterated. There are quite a few options here - The most common would be to either build a new collection from the original, or add items into a temporary collection while iterating, then add them all to the original collection at the end.
The only collections in the framework which are designed with iteration and insertion in mind are some of the concurrent collections. For example, you can be iterating a BlockingCollection<T> via GetConsumingEnumerable and Add items to it at the same time. However, this is intended for a different purpose - it's typically used when having a separate consumer and producer thread, one adding, while the other processes items. As such, doing this within its own loop would be a very odd use case.
No, you will get an exception. While it isn't recommended, you can accomplish what you're looking for by using a simple for loop. The reason you're getting an exception is because of how foreach works. When compiled it is actually using the IEnumerable<T> or IEnumerable that is implemented by the List<T> to get the items. Now you can create your own collection which would allow something like this, but again, not recommended.
foreach is only for seeing values of any collection, if you'll be change number of elements in collection - exception will be thrown. If you will be change values in collection - nothing will happen, but Microsoft advice not to use foreach for such case.
IF you need to change elements or number of elements use list.ToArray() and FOR cycle through the array.
Simply create a new list. For example with buttons:
List<Button> list = new List<Button>();
list.Add(new Button());
list.Add(new Button());
foreach (Button button in new List<Button>(list))
list.Add(new Button());
Not the best solution, but probably the easiest.
Some possibly good, some complicated solutions here. How about cyling though the list with a while based on the lists length and the current position, that should work?
UInt16 n = 0;
while (n < list.Count)
{
... // might add new elements to the end of the list
n++;
}

Is there any method or way in C# to add items in a List<T> on a LIFO basis?

this is an easy one. I have a List<T> in C# and I woudl like to add some elements on a LIFO basis, so on the "bottom" of the List<T>.
For several reasons I cannot use the Stack class.
Thanks
Francesco
Yes the Add() method adds to the end of the list, you can use RemoveAt(yourList.Count - 1) to remove last, and yourList[yourList.Count - 1] to peek at the last.
Though I'm curious, why can't you use the Stack() class?
Items added to a List<T> using the Add method are placed at the end of the list. If you want to process the list so that it's LIFO, either iterate in reverse (meaning you change the way you process the list) or always use Insert(0, item) to add elements to the list (meaning you change the way you populate the list).
You can insert anywhere in the list you'd like.
http://msdn.microsoft.com/en-us/library/sey5k5z4.aspx
Insert(0,T)
http://msdn.microsoft.com/en-us/library/sey5k5z4.aspx
This isn't a stack though. If you want to remove objects again as you use them, I'd consider extending a stack...
LIFO and FIFO is not a question of adding but a question of removing.
You can use List<T> and always pick up the last item:
List<int> list = Enumerable.Range(1,100).ToList();
While(list.Count>0)
{
list.RemoveAt(list.Count-1);
}

Why am I getting an InvalidOperationException?

foreach (var shotItem in Invadershots)// it points to me to there and doesnt allow me to loop.."{"Collection was modified; enumeration operation may not execute."}"
{
shotItem.Move();// it happens when this simple method called (which actually checks some bool..if the shot was out of the winform).
if (shotItem.removeShot)
{
Invadershots.Remove(shotItem);
}
}
Could it be because i change the List items simultaneously?
How can i prevent that error from occurring?
This is because you trying modify collection Invadershots
Invadershots.Remove(shotItem);
This is not allowed within foreach, use for instead..
You cannot alter a collection whilst enumerating across it. Create a clone of the collection and alter that.
You can't do that deleting an element into a List, that you'r reading in a foreach will crash, surely, try to make a copy to remove with that while you're in the foreach, or make a for iteration and control de number of elements correctly and the out condition.
See you

Enumerator problem, Any way to avoid two loops?

I have a third party api, which has a class that returns an enumerator for different items in the class.
I need to remove an item in that enumerator, so I cannot use "for each". Only option I can think of is to get the count by iterating over the enum and then run a normal for loop to remove the items.
Anyone know of a way to avoid the two loops?
Thanks
[update] sorry for the confusion but Andrey below in comments is right.
Here is some pseudo code out of my head that won't work and for which I am looking a solution which won't involve two loops but I guess it's not possible:
for each (myProperty in MyProperty)
{
if (checking some criteria here)
MyProperty.Remove(myProperty)
}
MyProperty is the third party class that implements the enumerator and the remove method.
Common pattern is to do something like this:
List<Item> forDeletion = new List<Item>();
foreach (Item i in somelist)
if (condition for deletion) forDeletion.Add(i);
foreach (Item i in forDeletion)
somelist.Remove(i); //or how do you delete items
Loop through it once and create a second array which contains the items which should not be deleted.
If you know it's a collection, you can go with reverted for:
for (int i = items.Count - 1; i >= 0; i--)
{
items.RemoveAt(i);
}
Otherwise, you'll have to do two loops.
You can create something like this:
public IEnumerable<item> GetMyList()
{
foreach (var x in thirdParty )
{
if (x == ignore)
continue;
yield return x;
}
}
I need to remove an item in that enumerator
As long as this is a single item that's not a problem. The rule is that you cannot continue to iterate after modifying the collection. Thus:
foreach (var item in collection) {
if (item.Equals(toRemove) {
collection.Remove(toRemove);
break; // <== stop iterating!!
}
}
It is not possible to remove an item from an Enumerator. What you can do is to copy or filter(or both) the content of the whole enumeration sequence.
You can achieve this by using linq and do smth like this:
YourEnumerationReturningFunction().Where(item => yourRemovalCriteria);
Can you elaborate on the API and the API calls you are using?
If you receive an IEnumerator<T> or IEnumerable<T> you cannot remove any item from the sequence behind the enumerator because there is no method to do so. And you should of course not rely on down casting an received object because the implementation may change. (Actually a well designed API should not expose mutable objects holding internal state at all.)
If you receive IList<T> or something similar you can just use a normal for loop from back to front and remove the items as needed because there is no iterator which state could be corrupted. (Here the rule about exposing mutable state should apply again - modifying the returned collection should not change any state.)
IEnumerator.Count() will decide at run-time what it needs to do - enumerate to count or reflect to see it's a collection and call .Count that way.
I like SJoerd's suggestion but I worry about how many items we may be talking about.
Why not something like ..
// you don't want 2 and 3
IEnumerable<int> fromAPI = Enumerable.Range(0, 10);
IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 });
A clean, readable way to do this is as follows (I'm guessing at the third-party container's API here since you haven't specified it.)
foreach(var delItem in ThirdPartyContainer.Items
.Where(item=>ShouldIDeleteThis(item))
//or: .Where(ShouldIDeleteThis)
.ToArray()) {
ThirdPartyContainer.Remove(delItem);
}
The call to .ToArray() ensures that all items to be deleted have been greedily cached before the foreach iteration begins.
Behind the scenes this involves an array and an extra iteration over that, but that's generally very cheap, and the advantage of this method over the other answers to this question is that it works on plain enumerables and does not involve tricky mutable state issues that are hard to read and easy to get wrong.
By contrast, iterating in reverse, while not rocket science, is much more prone to off-by-one errors and harder to read; and it also relies on internals of the collection such as not changing order in between deletions (e.g. better not be a binary heap, say). Manually adding items that should be deleted to a temporary list is just unnecessary code - that's what .ToArray() will do just fine :-).
an enumerator always has a private field pointing to the real collection.
you can get it via reflection.modify it.
have fun.

Categories