C# – Change a dictionary’s values in a foreach loop

In .NET 5 and above, you can directly change a dictionary’s values in a foreach loop. Here’s an example:

var wordCountMap = new Dictionary<string, int>() { ["apple"] = 1, ["cat"] = 5, ["dog"] = 10 }; foreach (var word in wordCountMap) { wordCountMap[word.Key]++; Console.WriteLine($"{word.Key}={wordCountMap[word.Key]}"); }
Code language: C# (cs)

This outputs the following:

apple=2 cat=6 dog=11
Code language: plaintext (plaintext)

You couldn’t do this before .NET 5, because it would invalidate the enumerator and throw an exception: InvalidOperationException: Collection was modified; enumeration operation my not execute. Instead, you’d have to make the changes indirectly. For example, to change all of the dictionary’s values, the most straightforward way to do that was to use .ToList(), like this:

using System.Linq; foreach (var word in wordCountMap.ToList()) { wordCountMap[word.Key]++; Console.WriteLine($"{word.Key}={wordCountMap[word.Key]}"); }
Code language: C# (cs)

This is inefficient and counterintuitive compared to the nice, direct way that you can do it in .NET 5 and above.

In addition to this change, you can directly remove items from a dictionary while looping (added in .NET Core 3.0). In other words, they made the enumerator smarter so it can handle these common scenarios in an intuitive way. I’ll show an example of that below, and also explain which modifications you can’t do to a dictionary while looping.

Remove from a dictionary while looping

In .NET Core 3.0 and above, you can directly remove items from a dictionary in a foreach loop (which simplifies operations such as filtering). Here’s an example:

using System.Linq; var animalDictionary = new Dictionary<string, int>() { ["fish"] = 0, ["cat"] = 1, ["dog"] = 2 }; foreach (var animal in animalDictionary.Where(kvp => kvp.Value == 0)) { animalDictionary.Remove(animal.Key); } Console.WriteLine($"There are {animalDictionary.Count} species of animals");
Code language: C# (cs)

This outputs the following, indicating that an item was removed from the dictionary:

There are 2 species of animals
Code language: plaintext (plaintext)

Before .NET Core 3.0, to avoid getting InvalidOperationException, you’d have to indirectly remove items by getting a list of items to remove and then looping through that list. Again, the most straightforward way to do that is to call .ToList(). Here’s an example:

using System.Linq; foreach (var word in animalDictionary.Where(kvp => kvp.Value == 0).ToList()) { animalDictionary.Remove(word.Key); }
Code language: C# (cs)

Can’t add items to a dictionary while looping over it

You still can’t add new items to the dictionary while directly looping over it. This results in it throwing an exception: InvalidOperationException: Collection was modified; enumeration operation my not execute.

This makes sense, because when you add a key/value pair to the dictionary, it can end up at any location in underlying collection(s) (because the key gets hashed). This means it’s possible for it to get added to a position that the active enumerator has already passed.

Instead, you can indirectly loop over the dictionary by calling .ToList() and add the items that way (or separately get a list of items to add and loop over that). Here’s an example:

using System.Linq; var dictionary = new Dictionary<string, int>() { ["a"] = 1, ["b"] = 2 }; foreach (var kvp in dictionary.ToList()) { dictionary.Add(kvp.Key + kvp.Key, 0); } Console.WriteLine($"There are {dictionary.Count} items");
Code language: C# (cs)

This outputs the following:

There are 4 items
Code language: plaintext (plaintext)

Can’t change the dictionary’s loop variable

When you change a dictionary’s value, you have to index into it:

foreach (var kvp in dictionary) { dictionary[kvp.Key]++; }
Code language: C# (cs)

You may be wondering, why do I have to index into the dictionary? Can’t I just change the value via the loop variable (i.e. word.Value++)?

No, you can’t. The loop variable is a KeyValuePair object. Think of this as a readonly snapshot from the dictionary. KeyValuePair.Value is the value at the time it was yielded from the dictionary. It doesn’t represent the current dictionary value. You can see this by looking at the KeyValuePair after changing the dictionary value:

var dictionary = new Dictionary<string, int>() { ["a"] = 1, ["b"] = 2 }; foreach (var kvp in dictionary) { dictionary[kvp.Key]++; Console.WriteLine($"key={kvp.Key} before={kvp.Value} after={dictionary[kvp.Key]}"); }
Code language: C# (cs)

This outputs:

key=a before=1 after=2 key=b before=2 after=3
Code language: plaintext (plaintext)

This is kind of nice if you want to know the before and after value (i.e. for logging, or perhaps for being able to undo the change).

Leave a Comment