我正试图从字典中建立一个饼图。在显示饼图之前,我想整理一下数据。我去掉了所有小于5%的派片,把它们放到“其他”派片里。然而,我得到一个集合被修改;枚举操作在运行时不能执行异常。
我理解为什么在遍历字典时不能从字典中添加或删除项。但是,我不明白为什么不能简单地在foreach循环中更改现有键的值。
任何建议:修复我的代码,将不胜感激。
Dictionary<string, int> colStates = new Dictionary<string,int>();
// ...
// Some code to populate colStates dictionary
// ...
int OtherCount = 0;
foreach(string key in colStates.Keys)
{
double Percent = colStates[key] / TotalCount;
if (Percent < 0.05)
{
OtherCount += colStates[key];
colStates[key] = 0;
}
}
colStates.Add("Other", OtherCount);
你在这一行修改集合:
col各州[键]= 0;
通过这样做,您实际上是在此时删除和重新插入一些内容(就IEnumerable而言)。
如果你编辑你存储的值的一个成员,那是可以的,但是你在编辑值本身,而IEnumberable不喜欢这样。
我使用的解决方案是消除foreach循环,只使用for循环。
简单的for循环不会检查您知道不会影响集合的更改。
你可以这样做:
List<string> keys = new List<string>(colStates.Keys);
for(int i = 0; i < keys.Count; i++)
{
string key = keys[i];
double Percent = colStates[key] / TotalCount;
if (Percent < 0.05)
{
OtherCount += colStates[key];
colStates[key] = 0;
}
}
如果你觉得有创意,你可以这样做。在字典中进行反向循环以进行更改。
Dictionary<string, int> collection = new Dictionary<string, int>();
collection.Add("value1", 9);
collection.Add("value2", 7);
collection.Add("value3", 5);
collection.Add("value4", 3);
collection.Add("value5", 1);
for (int i = collection.Keys.Count; i-- > 0; ) {
if (collection.Values.ElementAt(i) < 5) {
collection.Remove(collection.Keys.ElementAt(i)); ;
}
}
当然不完全相同,但你可能会感兴趣…
这个答案用于比较两个解决方案,而不是建议的解决方案。
您可以使用for循环,使用字典Count作为循环停止条件,并使用Keys.ElementAt(i)获取键,而不是像其他答案建议的那样创建另一个列表。
for (int i = 0; i < dictionary.Count; i++)
{
dictionary[dictionary.Keys.ElementAt(i)] = 0;
}
起初,我认为这将是更有效的,因为我们不需要创建一个键列表。在运行测试后,我发现for循环解决方案的效率要低得多。原因是ElementAt在字典上是O(n)。属性时,它从集合的开始搜索,直到找到第n个项。
测试:
int iterations = 10;
int dictionarySize = 10000;
Stopwatch sw = new Stopwatch();
Console.WriteLine("Creating dictionary...");
Dictionary<string, int> dictionary = new Dictionary<string, int>(dictionarySize);
for (int i = 0; i < dictionarySize; i++)
{
dictionary.Add(i.ToString(), i);
}
Console.WriteLine("Done");
Console.WriteLine("Starting tests...");
// for loop test
sw.Restart();
for (int i = 0; i < iterations; i++)
{
for (int j = 0; j < dictionary.Count; j++)
{
dictionary[dictionary.Keys.ElementAt(j)] = 3;
}
}
sw.Stop();
Console.WriteLine($"for loop Test: {sw.ElapsedMilliseconds} ms");
// foreach loop test
sw.Restart();
for (int i = 0; i < iterations; i++)
{
foreach (string key in dictionary.Keys.ToList())
{
dictionary[key] = 3;
}
}
sw.Stop();
Console.WriteLine($"foreach loop Test: {sw.ElapsedMilliseconds} ms");
Console.WriteLine("Done");
结果:
Creating dictionary...
Done
Starting tests...
for loop Test: 2367 ms
foreach loop Test: 3 ms
Done