[C#] foreach 루프에서 사전 값 편집

사전에서 원형 차트를 작성하려고합니다. 원형 차트를 표시하기 전에 데이터를 정리하고 싶습니다. 파이의 5 % 미만인 파이 조각을 제거하고 “기타”파이 조각에 넣습니다. 그러나 Collection was modified; enumeration operation may not execute런타임에 예외 가 발생합니다.

반복하는 동안 사전에서 항목을 추가하거나 제거 할 수없는 이유를 이해합니다. 그러나 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);



답변

사전에 값을 설정하면 내부 “버전 번호”가 업데이트되어 반복자와 키 또는 값 콜렉션과 연관된 반복자가 무효화됩니다.

나는 당신의 요점을 알지만, 동시에 값 컬렉션이 반복 도중에 변경 될 수 있다면 이상 할 것입니다-그리고 단순화를 위해 버전 번호가 하나뿐입니다.

이러한 종류의 문제를 해결하는 일반적인 방법은 키 컬렉션을 미리 복사하고 복사본을 반복하거나 원본 컬렉션을 반복하지만 반복을 마친 후에 적용 할 변경 사항 컬렉션을 유지하는 것입니다.

예를 들면 다음과 같습니다.

키를 먼저 복사

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

또는…

수정 목록 작성

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


답변

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


답변

이 줄에서 컬렉션을 수정하고 있습니다.

colStates [키] = 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;    
    }
}


답변

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


답변

사전에 대해 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);
}


답변

창의력이 있다면 이와 같은 일을 할 수 있습니다. 사전을 거꾸로 반복하여 변경하십시오.

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

}

확실히 동일하지는 않지만 어쨌든 관심이있을 수 있습니다 …


답변

기존 위치에서 수정하지 않고 새 사전을 작성해야합니다. 키 조회를 사용하지 않고 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;