如何遍历字典并更改值?


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

我收到一个错误:“无法将属性或索引器'System.Collections.Generic.KeyValuePair.Value'分配给它-这是只读的。”
如何遍历myDict和更改值?


1
如果从性能的角度(间接性和更大的gc压力)添加很多参考对象是可以接受的,并且您可以控制字典,则为您的值创建一个框将解决此问题,例如:class Box<T> { public T boxed; }然后使用a Dictionary<string, Box<double>>,然后可以在foreach中“修改”类似:kvp.Value.boxed = 123.456;。并不是说这是“最佳”方法,但是它有其用途。
AnorZaken '16

这回答了你的问题了吗?在foreach循环中编辑字典值
马尔科姆

Answers:


111

根据MSDN

foreach语句是枚举器的包装,它仅允许从集合中读取内容,而不能对其进行写入。

用这个:

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);
}

7
我在.NET 2和3.5中测试了您的示例,并抛出“ Collection was Modifyed exception”。请参阅:stackoverflow.com/questions/1562729/….NET4中 是否已对此进行了更改,或者您没有测试示例?
灰烬

3
多么尴尬-我省去了填充列表的部分。立即修复。这里的想法是您可以更改字典条目的值,而不能更改其引用。
Justin R.

希望您将问题注释行保留为“ // ...(填充)”。一览表是空的。
crokusek

1
顺便说一句,你可以只做dictionary.Keys.ToList()
Bojidar Stanchev

38

对于懒惰的程序员:

Dictionary<string, double> dictionary = new Dictionary<string, double>();
foreach (var key in dictionary.Keys.ToList())
{
   dictionary[key] = Math.Round(dictionary[key], 3);
}

2
由于我现在正在使用.NET 4.5进行编码,因此该ToList()方法不可用,但是该Keys成员是可迭代的,因此.ToList()是不必要的。
Mike C

4
@MikeC请参阅stackoverflow.com/a/2260462/1037948上的评论-您不能直接枚举密钥,这.ToList()是解决问题的“方法”
drzaus

3
ToList()是LINQ扩展方法。如果添加“ using System.Linq;”,它应该可用。到您的using语句。
bcwhims 2015年

@drzaus:为什么不能列举Keys?键是一本字典,可以像其他任何字典一样枚举。我没有.ToList()。
Veverke

7
我知道这是一个旧答案,但我更喜欢它而不是公认的答案–我要说的不是简洁,而是更简洁(删去明确声明键列表的行)。因此,要回答上述问题:您可以枚举key集合,但是问题在于枚举和进行更改,而您不能这样做。添加ToList()意味着您实际上在枚举一个列表,该列表恰好包含与字典中的键相同的对象。这使字典本身变得可变,因此可以进行更改。
凯特

8

您不应该在迭代字典时更改字典,否则会出现异常。

因此,首先将键值对复制到临时列表,然后遍历该临时列表,然后更改字典:

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();

输出(在我的机器上):

a = 2.2
b = 77777.333
c = 2.346

注意:就性能而言,此解决方案比当前发布的解决方案要好一些,因为已经为键分配了该值,并且无需再次从字典对象中获取它。


1
这可能是我将要采用的方法,但是很想知道复制完整词典的开销。
艾伯托2015年

4

过了一段时间,但也许有人对此感兴趣:

yourDict = yourDict.ToDictionary(kv => kv.Key, kv => Math.Round(kv.Value, 3))

可读性强的版本!添加错误检查时,可能会变得有些混乱。
尖锐的

2

我注意到,使用Modify遍历Dictionary的最快方法(目前)是:

//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;
}

第一个大约需要2.2秒,第二个大约需要4.5秒(经过测试的字典大小为1000,并重复了10k次,将字典大小更改为10不会改变比率)。同样,获取Key列表也没什么大不了的,dictionary [key]的值获取只是内置于迭代中的慢速VS。另外,如果您想提高速度,可以使用硬编码类型来哑(“ Test”)类,这样我就得到了约1.85s(硬编码为“ object”)。

编辑:

安娜之前也发布了相同的解决方案:https : //stackoverflow.com/a/6515474/766304


1

一种解决方案是预先将键放在列表(或另一个集合)中,并在更改字典时遍历它们:

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);
}

-1

尽管由于出现异常(例如Ron已经说过)而无法直接在字典上进行迭代,但是您不需要使用临时列表来解决问题。

相反,不要使用foreach,而是使用for循环来遍历字典并使用索引访问来更改值:

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);
}

尽管这可以工作,但是Enumerable.ElementAt()-这是您正在使用的扩展方法-是O(n)对non-IList<>的操作
Evgeniy Berezovsky 2015年

将明确说出尤金·别列索夫斯基在说什么。最终至少为O(n ^ 2)。因此,这是一个非常糟糕的解决方案。
Pavel Chikulaev 2015年

-2

循环浏览字典中的键,而不是KeyValuePairs。

Dictionary<string, double> myDict = new Dictionary<string, double>();
//...
foreach (string key in myDict.Keys)
{
    myDict[key] = Math.Round(myDict[key], 3);
}

9
令人惊讶的是,它引发了“ Collection Modify ... ”异常。
Slauma 2014年

@Slauma确实令人惊讶,因为此更改不是结构性更改,因此实际上没有必要进行枚举数抛出。
Evgeniy Berezovsky 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.