C# 在 foreach 循环中编辑字典值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1070766/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:32:17  来源:igfitidea点击:

Editing dictionary values in a foreach loop

c#.net.net-2.0

提问by Aheho

I am trying to build a pie chart from a dictionary. Before I display the pie chart, I want to tidy up the data. I'm removing any pie slices that would be less than 5% of the pie and putting them in a "Other" pie slice. However I'm getting a Collection was modified; enumeration operation may not executeexception at runtime.

我正在尝试从字典中构建饼图。在显示饼图之前,我想整理一下数据。我正在移除任何小于馅饼 5% 的馅饼片,并将它们放入“其他”馅饼片中。但是我Collection was modified; enumeration operation may not execute在运行时遇到异常。

I understand why you can not add or remove items from a dictionary while iterating over them. However I don't understand why you can't simply change a value for an existing key within the foreach loop.

我理解为什么在迭代它们时不能从字典中添加或删除项目。但是我不明白为什么您不能简单地更改 foreach 循环中现有键的值。

Any suggestions re: fixing my code, would be appreciated.

任何建议:修复我的代码,将不胜感激。

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

采纳答案by Jon Skeet

Setting a value in a dictionary updates its internal "version number" - which invalidates the iterator, and any iterator associated with the keys or values collection.

在字典中设置值会更新其内部“版本号”——这会使迭代器以及与键或值集合关联的任何迭代器无效。

I do see your point, but at the same time it would be odd if the values collection could change mid-iteration - and for simplicity there's only one version number.

我确实理解您的观点,但与此同时,如果值集合可以在迭代过程中改变,那就很奇怪了 - 为简单起见,只有一个版本号。

The normal way of fixing this sort of thing is to either copy the collection of keys beforehand and iterate over the copy, or iterate over the original collection but maintain a collection of changes which you'll apply after you've finished iterating.

解决此类问题的正常方法是事先复制键集合并迭代副本,或者迭代原始集合但维护一组更改,您将在完成迭代后应用这些更改。

For example:

例如:

Copying keys first

首先复制密钥

List<string> keys = new List<string>(colStates.Keys);
foreach(string key in keys)
{
    double percent = colStates[key] / TotalCount;    
    if (percent < 0.05)
    {
        OtherCount += colStates[key];
        colStates[key] = 0;
    }
}

Or...

或者...

Creating a list of modifications

创建修改列表

List<string> keysToNuke = new List<string>();
foreach(string key in colStates.Keys)
{
    double percent = colStates[key] / TotalCount;    
    if (percent < 0.05)
    {
        OtherCount += colStates[key];
        keysToNuke.Add(key);
    }
}
foreach (string key in keysToNuke)
{
    colStates[key] = 0;
}

回答by Richard

You need to create a new Dictionary from the old rather than modifying in place. Somethine like (also iterate over the KeyValuePair<,> rather than using a key lookup:

您需要从旧字典创建一个新字典,而不是就地修改。类似的东西(也迭代 KeyValuePair<,> 而不是使用键查找:

int otherCount = 0;
int totalCounts = colStates.Values.Sum();
var newDict = new Dictionary<string,int>();
foreach (var kv in colStates) {
  if (kv.Value/(double)totalCounts < 0.05) {
    otherCount += kv.Value;
  } else {
    newDict.Add(kv.Key, kv.Value);
  }
}
if (otherCount > 0) {
  newDict.Add("Other", otherCount);
}

colStates = newDict;

回答by Cambium

Disclaimer: I don't do much C#

免责声明:我不怎么做 C#

You are trying to modify the DictionaryEntry object which is stored in the HashTable. The Hashtable only stores one object -- your instance of DictionaryEntry. Changing the Key or the Value is enough to change the HashTable and cause the enumerator to become invalid.

您正在尝试修改存储在 HashTable 中的 DictionaryEntry 对象。Hashtable 只存储一个对象——您的 DictionaryEntry 实例。更改 Key 或 Value 足以更改 HashTable 并导致枚举器无效。

You can do it outside of the loop:

您可以在循环之外执行此操作:

if(hashtable.Contains(key))
{
    hashtable[key] = value;
}

by first creating a list of all the keys of the values you wish to change and iterate through that list instead.

首先创建一个包含您希望更改的值的所有键的列表,然后遍历该列表。

回答by Samuel Carrijo

You can't modify the collection, not even the values. You could save these cases and remove them later. It would end up like this:

您不能修改集合,甚至不能修改值。您可以保存这些案例并稍后将其删除。最终会是这样:

        Dictionary<string, int> colStates = new Dictionary<string, int>();
        // ...
        // Some code to populate colStates dictionary
        // ...

        int OtherCount = 0;
        List<string> notRelevantKeys = new List<string>();

        foreach (string key in colStates.Keys)
        {

            double Percent = colStates[key] / colStates.Count;

            if (Percent < 0.05)
            {
                OtherCount += colStates[key];
                notRelevantKeys.Add(key);
            }
        }

        foreach (string key in notRelevantKeys)
        {
            colStates[key] = 0;
        }

        colStates.Add("Other", OtherCount);

回答by CodeFusionMobile

You are modifying the collection in this line:

您正在修改此行中的集合:

colStates[key] = 0;

colStates[key] = 0;

By doing so, you are essentially deleting and reinserting something at that point (as far as IEnumerable is concerned anyways.

通过这样做,您实际上是在那时删除并重新插入某些内容(就 IEnumerable 而言,无论如何。

If you edit a memberof the value you are storing, that would be OK, but you are editing the value itself and IEnumberable doesn't like that.

如果您编辑要存储的值的成员,那没问题,但是您正在编辑值本身,而 IEnumberable 不喜欢那样。

The solution I've used is to eliminate the foreach loop and just use a for loop. A simple for loop won't check for changes that you know won't effect the collection.

我使用的解决方案是消除 foreach 循环并只使用 for 循环。一个简单的 for 循环不会检查您知道不会影响集合的更改。

Here's how you could do it:

你可以这样做:

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

回答by Jeremy Frey

You can't modify the keys nor the values directly in a ForEach, but you can modify their members. E.g., this should work:

您不能直接在 ForEach 中修改键和值,但可以修改它们的成员。例如,这应该有效:

public class State {
    public int Value;
}

...

Dictionary<string, State> colStates = new Dictionary<string,State>();

int OtherCount = 0;
foreach(string key in colStates.Keys)
{
    double  Percent = colStates[key].Value / TotalCount;

    if (Percent < 0.05)
    {
        OtherCount += colStates[key].Value;
        colStates[key].Value = 0;
    }
}

colStates.Add("Other", new State { Value =  OtherCount } );

回答by Scott Ivey

How about just doing some linq queries against your dictionary, and then binding your graph to the results of those?...

仅对您的字典进行一些 linq 查询,然后将您的图形绑定到这些结果如何?...

var under = colStates.Where(c => (decimal)c.Value / (decimal)totalCount < .05M);
var over = colStates.Where(c => (decimal)c.Value / (decimal)totalCount >= .05M);
var newColStates = over.Union(new Dictionary<string, int>() { { "Other", under.Sum(c => c.Value) } });

foreach (var item in newColStates)
{
    Console.WriteLine("{0}:{1}", item.Key, item.Value);
}

回答by Hugoware

If you're feeling creative you could do something like this. Loop backwards through the dictionary to make your changes.

如果你觉得有创意,你可以做这样的事情。在字典中向后循环以进行更改。

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

}

Certainly not identical, but you might be interested anyways...

当然不完全相同,但无论如何你可能会感兴趣......

回答by DIG

Call the ToList()in the foreachloop. This way we dont need a temp variable copy. It depends on Linq which is available since .Net 3.5.

ToList()foreach循环中调用。这样我们就不需要临时变量副本。这取决于从 .Net 3.5 开始可用的 Linq。

using System.Linq;

foreach(string key in colStates.Keys.ToList())
{
  double  Percent = colStates[key] / TotalCount;

    if (Percent < 0.05)
    {
        OtherCount += colStates[key];
        colStates[key] = 0;
    }
}

回答by Nick Louloudakis

You can make a list copy of the dict.Values, then you can use the List.ForEachlambda function for iteration, (or a foreachloop, as suggested before).

您可以制作 的列表副本dict.Values,然后您可以使用List.ForEachlambda 函数进行迭代(或foreach之前建议的循环)。

new List<string>(myDict.Values).ForEach(str =>
{
  //Use str in any other way you need here.
  Console.WriteLine(str);
});