2

I want to change all the values that fulfill a certain criterium in a C# dictionary.

Simply editing the values like this

foreach (var kv in dictionary)
{
   kv.Value += 1;
}

does not work because the KeyValuePair of the foreach loop is read only.

However, editing the entries directly like this:

foreach (var kv in dictionary)
{
   dictionary[kv.Key] = kv.Value + 1;
}

also doesn't work, because it modifies the collection and breaks the iterator.

At this point, the only remaining solution I can think of is storing all keys of the dictionary in a list, and then using that to edit the values during a second loop, however, that seems like a pretty inelegant solution to me.

Is there any better alternative?

Mohammed Sajid
  • 4,778
  • 2
  • 15
  • 20
  • 3
    N.B. A [criterium](https://en.wikipedia.org/wiki/Criterium) is a type of bicycle race. You want a *criterion*. – Andrew Morton Jun 10 '20 at 22:12
  • 1
    Have you considered using `ConcurrentDictionary` instead? – mjwills Jun 10 '20 at 22:21
  • 1
    Non native speaker, sorry. The German word is "Kriterium". – Hellothere _1 Jun 10 '20 at 22:22
  • 1
    In VB.NET you could have `x = x.Select(Of DictionaryEntry)(Function(de) New DictionaryEntry With {.Key = de.Key, .Value = de.Value + 1}).ToDictionary(Of String, Integer)(Function(k) k.Key.ToString(), Function(v) CInt(v.Value))` - the translation to C# eludes me. – Andrew Morton Jun 10 '20 at 22:38
  • 1
    Related: [Collection was modified; enumeration operation may not execute](https://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute) – Theodor Zoulias Jun 10 '20 at 23:49

2 Answers2

4

You could create an array (or a List with .ToList()) from the .Keys in the foreach, something like this:

foreach (string key in dictionary.Keys.ToArray())
{
    dictionary[key] += 1;
}
devcrp
  • 1,323
  • 7
  • 17
  • 2
    Or, `.ToList()`. I would have thought that `dictionary.Keys` would fabricate a collection on the fly, but I guess it just implements an `IEnumerable` using a `yield return` (or something along those lines). – Flydog57 Jun 10 '20 at 22:18
  • 1
    Right, or `ToList()`, I included it in my answer, thank you @Flydog57 ! – devcrp Jun 10 '20 at 22:22
  • 1
    Right. I did think of that before, but I'm a dummy who forgot to include System.Linq, so I thought dictionaries don't have that option. – Hellothere _1 Jun 10 '20 at 22:25
  • 1
    I tested it and yes, otherwise the exception about modifying the collection is thrown. – devcrp Jun 10 '20 at 22:49
3

You can use ToDictionary to create new Dictionary and change the origin in the loop, like :

foreach (var kv in dictionary.ToDictionary(k=>k.Key,v=>v.Value))
{
   dictionary[kv.Key] = kv.Value + 1;
}

I hope you find this helpful.

Mohammed Sajid
  • 4,778
  • 2
  • 15
  • 20
  • 1
    I'm a big dummy. I did think of that, but it didn't seem to work. The real problem was that I forgot to include System.Linq for some reason. – Hellothere _1 Jun 10 '20 at 22:19