IT story

foreach 루프에서 사전 값 편집

hot-time 2020. 5. 20. 07:57
반응형

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;

값은 물론 컬렉션도 수정할 수 없습니다. 이 사례를 저장하고 나중에 제거 할 수 있습니다. 다음과 같이 끝납니다.

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

면책 조항 : 나는 C #을별로하지 않습니다.

HashTable에 저장된 DictionaryEntry 개체를 수정하려고합니다. Hashtable은 DictionaryEntry 인스턴스 하나의 개체 만 저장합니다. 키 또는 값을 변경하면 HashTable을 변경하고 열거자가 유효하지 않게됩니다.

루프 외부에서 할 수 있습니다.

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

먼저 변경하려는 값의 모든 키 목록을 작성하고 해당 목록을 반복하십시오.


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

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

Starting with .NET 4.5 You can do this with ConcurrentDictionary:

using System.Collections.Concurrent;

var colStates = new ConcurrentDictionary<string,int>();
colStates["foo"] = 1;
colStates["bar"] = 2;
colStates["baz"] = 3;

int OtherCount = 0;
int TotalCount = 100;

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

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

colStates.TryAdd("Other", OtherCount);

Note however that its performance is actually much worse that a simple foreach dictionary.Kes.ToArray():

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class ConcurrentVsRegularDictionary
{
    private readonly Random _rand;
    private const int Count = 1_000;

    public ConcurrentVsRegularDictionary()
    {
        _rand = new Random();
    }

    [Benchmark]
    public void ConcurrentDictionary()
    {
        var dict = new ConcurrentDictionary<int, int>();
        Populate(dict);

        foreach (var key in dict.Keys)
        {
            dict[key] = _rand.Next();
        }
    }

    [Benchmark]
    public void Dictionary()
    {
        var dict = new Dictionary<int, int>();
        Populate(dict);

        foreach (var key in dict.Keys.ToArray())
        {
            dict[key] = _rand.Next();
        }
    }

    private void Populate(IDictionary<int, int> dictionary)
    {
        for (int i = 0; i < Count; i++)
        {
            dictionary[i] = 0;
        }
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        BenchmarkRunner.Run<ConcurrentVsRegularDictionary>();
    }
}

Result:

              Method |      Mean |     Error |    StdDev |
--------------------- |----------:|----------:|----------:|
 ConcurrentDictionary | 182.24 us | 3.1507 us | 2.7930 us |
           Dictionary |  47.01 us | 0.4824 us | 0.4512 us |

Along with the other answers, I thought I'd note that if you get sortedDictionary.Keys or sortedDictionary.Values and then loop over them with foreach, you also go through in sorted order. This is because those methods return System.Collections.Generic.SortedDictionary<TKey,TValue>.KeyCollection or SortedDictionary<TKey,TValue>.ValueCollection objects, which maintain the sort of the original dictionary.

참고URL : https://stackoverflow.com/questions/1070766/editing-dictionary-values-in-a-foreach-loop

반응형