79
Dictionary<string,double> myDict = new Dictionary();
//...
foreach (KeyValuePair<string,double> kvp in myDict)
 {
     kvp.Value = Math.Round(kvp.Value, 3);
}

I get an error: "Property or indexer 'System.Collections.Generic.KeyValuePair.Value' cannot be assigned to -- it is read only."
How can I iterate through myDict and change values?

Mediator
  • 14,951
  • 35
  • 113
  • 191
Sergey
  • 47,222
  • 25
  • 87
  • 129
  • 1
    If adding a lot of reference objects is acceptable from a performance perspective (indirection and more gc-pressure) _and_ you have control over the dictionary then creating a box for your values would solve the problem, e.g: `class Box { public T boxed; }` and then use a `Dictionary>` which you can then "modify" in the foreach something like: `kvp.Value.boxed = 123.456;`. Not saying it is the "best" approach but it has its share of uses. – AnorZaken Apr 04 '16 at 00:42
  • Does this answer your question? [Editing dictionary values in a foreach loop](https://stackoverflow.com/questions/1070766/editing-dictionary-values-in-a-foreach-loop) – Malcolm Mar 11 '20 at 15:41

8 Answers8

118

According to MSDN:

The foreach statement is a wrapper around the enumerator, which allows only reading from the collection, not writing to it.

Use this:

var dictionary = new Dictionary<string, double>();
// TODO Populate your dictionary here
var keys = new List<string>(dictionary.Keys);
foreach (string key in keys)
{
   dictionary[key] = Math.Round(dictionary[key], 3);
}
Justin R.
  • 23,435
  • 23
  • 108
  • 157
  • 7
    I tested your example in .NET 2 and 3.5 and it throws 'Collection was modified exception'. See: http://stackoverflow.com/questions/1562729/why-cant-we-change-values-of-a-dictionary-while-enumerating-its-keys Has this changed in .NET 4 or did you not test your example? – Ash Feb 19 '11 at 03:39
  • 3
    How embarrassing - I left out the part where the list is populated. Fixed now. The idea here is that you can change the dictionary entry's value, just not its reference. – Justin R. Nov 08 '11 at 19:00
  • Wished you kept the question comment line with "//... (Populate)". At quick glance the list is empty. – crokusek Jun 29 '17 at 00:16
  • 1
    btw you can just do dictionary.Keys.ToList() – Bojidar Stanchev Aug 13 '20 at 10:55
  • 1
    In .NET 5 you no longer need an extra list. – tymtam Apr 07 '21 at 12:51
43

For the lazy programmers:

Dictionary<string, double> dictionary = new Dictionary<string, double>();
foreach (var key in dictionary.Keys.ToList())
{
   dictionary[key] = Math.Round(dictionary[key], 3);
}
NielsSchroyen
  • 449
  • 4
  • 4
  • 3
    As I'm coding now with .NET 4.5, the `ToList()` method is unavailable, but the `Keys` member is iterable so `.ToList()` is unnecessary. – Mike C Feb 13 '15 at 22:58
  • 4
    @MikeC see comment on http://stackoverflow.com/a/2260462/1037948 -- you can't directly enumerate on the keys, the `.ToList()` is a "hack" to get around that – drzaus Jul 30 '15 at 19:13
  • 3
    ToList() is a LINQ extension method. It should be available if you add "using System.Linq;" to your using statements. – bcwhims Sep 25 '15 at 14:03
  • @drzaus: Why can't you enumerate over Keys ? Keys is a dictionary, being enumerable just like any other dictionary. I am doing it without .ToList(). – Veverke Dec 24 '15 at 17:08
  • 9
    I know this is an old answer but I prefer it to the accepted one - I'd say rather than being lazy it's more concise (cuts out the line explicitly declaring the list of keys). So, to answer the question above: you can enumerate over the keys collection, but the question is about enumerating and making changes, which you can't do. Adding the ToList() means you're actually enumerating a list that happens to contain the same objects as the keys in the dictionary. This leaves the dictionary itself mutable and so allows you to make changes. – Kate Jun 03 '16 at 11:07
7

You shouldn't change the dictionary while iterating it, otherwise you get an exception.

So first copy the key-value pairs to a temp list and then iterate through this temp list and then change your dictionary:

Dictionary<string, double> myDict = new Dictionary<string, double>();

// a few values to play with
myDict["a"] = 2.200001;
myDict["b"] = 77777.3333;
myDict["c"] = 2.3459999999;

// prepare the temp list
List<KeyValuePair<string, double>> list = new List<KeyValuePair<string, double>>(myDict);

// iterate through the list and then change the dictionary object
foreach (KeyValuePair<string, double> kvp in list)
{
    myDict[kvp.Key] = Math.Round(kvp.Value, 3);
}


// print the output
foreach (var pair in myDict)
{
    Console.WriteLine(pair.Key + " = " + pair.Value);
}

// uncomment if needed
// Console.ReadLine();

output (on my machine):

a = 2.2
b = 77777.333
c = 2.346

Note: in terms of performance, this solution is a bit better than currently posted solutions, since the value is already assigned with the key, and there's no need to fetch it again from the dictionary object.

Ron Klein
  • 9,178
  • 9
  • 55
  • 88
  • 1
    That is probably the approach I will follow, but would love to know the overhead of copying the full dictionary. – Alberto May 24 '15 at 15:10
6

passed some time, but maybe someone is interested in it:

yourDict = yourDict.ToDictionary(kv => kv.Key, kv => Math.Round(kv.Value, 3))
user3104267
  • 190
  • 2
  • 10
2

I noticed that fastest way (at this moment) iterate over Dictionary with modify is:

//Just a dumb class
class Test<T>
{
    public T value;

    public Test() { }
    public Test(T v) { value = v; }
}

Dictionary<int, Test<object>> dic = new Dictionary<int, Test<object>>();
//Init dictionary
foreach (KeyValuePair<int, Test> pair in dic)
{
    pair.Value.value = TheObject;//Modify
}

VS

List<int> keys = new List<int>(dic.Keys); //This is fast operation   
foreach (int key in keys)
{
    dic[key] = TheObject;
}

First one takes about 2.2s and second one 4.5s (tested dictionary size of 1000 and repeated 10k time, changing dictionary size to 10 didn't change the ratios). Also there wasn't a big deal with getting the Key list, dictionary[key] value get is just slow VS built in iteration. Also if you want even more speed use hard coded type to dumb ("Test") class, with that I got it about 1.85s (with hard coded to "object").

EDIT:

Anna has posted the same solution before: https://stackoverflow.com/a/6515474/766304

Community
  • 1
  • 1
Risord
  • 2,116
  • 1
  • 15
  • 13
1

One solution would be to put the keys in a list (or another collection) beforehand and iterate through them while changing the dictionary:

Dictionary<string, double> dictionary = new Dictionary<string, double>();

// Populate it
List<string> keys = new List<string>(dictionary.Keys);

foreach (string key in keys)
{
   dictionary[key] = Math.Round(dictionary[key], 3);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hypno
  • 11
  • 1
-1

While iterating over the dictionary directly is not possible because you get an exception (like Ron already said), you don't need to use a temp list to solve the problem.

Instead use not the foreach, but a for loop to iterate through the dictionary and change the values with indexed access:

Dictionary<string, double> myDict = new Dictionary<string,double>();
//...    
for(int i = 0; i < myDict.Count; i++) {
    myDict[myDict.ElementAt(i).Key] = Math.Round(myDict.ElementAt(i).Value, 3);
}
Desty
  • 2,684
  • 21
  • 28
  • Although this will work, `Enumerable.ElementAt()` - which is the extension method you are using - is an [`O(n)` operation for non-`IList<>`s](http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,7db56d44563d8761,references). – Evgeniy Berezovsky Jan 07 '15 at 06:58
  • Will say explicitly what Eugene Beresovsky is saying. This ends up being at least O(n^2). So this is a very bad solution. – Pavel Chikulaev Nov 20 '15 at 02:03
-3

Loop through the keys in the dictionary, not the KeyValuePairs.

Dictionary<string, double> myDict = new Dictionary<string, double>();
//...
foreach (string key in myDict.Keys)
{
    myDict[key] = Math.Round(myDict[key], 3);
}
Ken D
  • 5,880
  • 2
  • 36
  • 58
Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179