tags:

views:

55

answers:

3

I have a simple Dictionary(of String, Object) that I need to iterate through and change items depending on some conditions.

As I can't modify a collection that I'm iterating through, how do I achieve this?

For example, the following obviously causes an Invalid Operation Exception:

Dim mOptions As New Dictionary(of String, Object)
mOptions.Add("optA", "A")
mOptions.Add("optB", "B")
mOptions.Add("optC", "C")

For Each O As KeyValuePair(Of String, Object) In mOptions
    Dim Val As Object = GetSomeOtherObjectBasedOnTheOption(O.Key, O.Value)
    mOptions(O.Key) = Val
Next

Invalid Operation Exception
Collection was modified; enumeration operation may not execute.

I guess I need to Clone the Dictionary first and iterate over the copy? What's the best way of doing that?

Dim TempOptions As New Dictionary(of String, Object)
For Each O As KeyValuePair(Of String, Object) In mOptions
    TempOptions.Add(O.Key, O.Value)
Next

For Each O As KeyValuePair(Of String, Object) In TempOptions
    Dim Val As Object = GetSomeOtherObjectBasedOnTheOption(O.Key, O.Value)
    mOptions(O.Key) = Val
Next

That smells a bit though.

A: 

Why iterate over KeyValuePairs?

Why not do this?

For Each K As String In mOptions.Keys.ToArray()
  Dim Val As Object = GetSomeOtherObjectBasedOnTheOption(K)
  mOptions(K) = Val
Next

This way, the collection your iterating over is a list of strings which can be used to access the Dictionary, not the Dictionary itself.

Apologies in advance for any crap VB. Haven't done it in years

Paul Alan Taylor
+4  A: 

You can just iterate over a copy of the keys instead of iterating over the KeyValuePairs.

For Each K as String in mOptions.Keys.ToArray()
   Dim Val As Object = GetSomeOtherObjectBasedOnTheOption(K)
   mOptions(K) = Val
Next

(sorry if you can't just paste that in -- I don't normally write VB)

It doesn't strictly have to be an array: you can do the VB equivalent of foreach (string k in new List<string>(mOptions.Keys)) as well, for instance.

If you iterate over the original keys and modify your dictionary, you'll get the same error.

Mark Rushakoff
I think I was doing this the wrong way around. I need the keys _and_ the values, not just the keys, but your approach works fine - I just get the current val from the original collection at the top of the loop.
Cylindric
A: 

in C# this can be done by using the yield pattern, don't know if it's also possible in VB.NET, otherwise you could create a custom IEnumerable that allows it...

Tim Mahy
Negative vote from me because this answer is phrased in a potentially misleading way, and focuses on C# instead of on a working VB.NET solution. The meaning would have been clearer if you had said it the other way around: _"This can be done by implementing a custom `IEnumerable`. Use `yield` for that purpose (if it is available in VB.NET)."_ (`yield` is not available in VB.NET btw., at least not in VS 2008.) -- However that may be, I think writing a custom iterator would be overkill, and therefore a bad idea. There's easier solutions.
stakx