각 루프에 대한 사전 값 편집
저는 사전으로 파이 차트를 만들려고 합니다.파이 차트를 표시하기 전에 데이터를 정리하고 싶습니다.파이의 5% 미만인 파이 조각을 제거하고 "기타" 파이 조각에 넣습니다.하지만 나는 받고 있습니다.Collection was modified; enumeration operation may not execute
런타임에 예외가 발생합니다.
항목을 반복하는 동안 사전에서 항목을 추가하거나 제거할 수 없는 이유를 이해합니다.하지만 각 루프에 대한 기존 키의 값을 단순히 변경할 수 없는 이유를 이해할 수 없습니다.
어떤 제안이든 제 코드를 수정해 주시면 감사하겠습니다.
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
루프. 이렇게 하면 임시 변수 복사본이 필요하지 않습니다.이후에 사용할 수 있는 Linq에 따라 다릅니다.네트 3.5.
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[key] = 0;
그렇게 함으로써, 당신은 본질적으로 그 시점에서 무언가를 삭제하고 다시 삽입하는 것입니다(IEnumberable에 관한 한).
저장 중인 값의 멤버를 편집하는 경우에는 괜찮지만 값 자체를 편집하는 것이므로 IEnumberable은 이를 좋아하지 않습니다.
제가 사용한 솔루션은 각 루프에 대한 for 루프를 제거하고 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 } );
.NET 5에서는 사전이 열거되는 동안 사전 항목을 변경할 수 있습니다.
꺼내기 요청은: 열거 중 사전 덮어쓰기 허용이며 문제는 사전의 덮어쓰기에서 _version++을 제거하는 것입니다.<TKey, TValue >.
이제 다음을 수행할 수 있습니다.
foreach (var pair in dict)
dict[pair.Key] = pair.Value + 1;
사전에 대해 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)); ;
}
}
확실히 똑같지는 않지만, 어쨌든 관심이 있을 겁니다
제자리에서 수정하는 대신 이전 사전을 새로 만들어야 합니다.(키 검색을 사용하는 대신 키 값 쌍<,>을 통해 반복됩니다.)
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;
.NET 4.5 버전 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);
그러나 실제로는 단순한 것보다 성능이 훨씬 더 나쁘다는 점에 유의하십시오.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>();
}
}
결과:
Method | Mean | Error | StdDev |
--------------------- |----------:|----------:|----------:|
ConcurrentDictionary | 182.24 us | 3.1507 us | 2.7930 us |
Dictionary | 47.01 us | 0.4824 us | 0.4512 us |
컬렉션은 수정할 수 없으며 값도 수정할 수 없습니다.이러한 사례를 저장하고 나중에 제거할 수 있습니다.다음과 같은 결과를 초래할 수 있습니다.
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#를 많이 하지 않습니다.
해시 테이블에 저장된 DictionaryEntry 개체를 수정하려고 합니다.해시 테이블에는 DictionaryEntry의 인스턴스인 하나의 개체만 저장됩니다.키 또는 값을 변경하면 해시 테이블을 변경하고 열거자를 비활성화하기에 충분합니다.
루프 외부에서 수행할 수 있습니다.
if(hashtable.Contains(key))
{
hashtable[key] = value;
}
먼저 변경할 값의 모든 키 목록을 만들고 해당 목록을 통해 반복합니다.
당신은 의목복을만수다있의 수 .dict.Values
그러면 당신은 그것을 사용할 수 있습니다.List.ForEach
( 반에대람또함수다한는복)또함((는▁aa))foreach
루프(앞에서 제안한 바와 같이).
new List<string>(myDict.Values).ForEach(str =>
{
//Use str in any other way you need here.
Console.WriteLine(str);
});
다른 답변들과 함께, 만약 당신이 그 정보를 얻는다면, 저는 그것을 주목할 것이라고 생각했습니다.sortedDictionary.Keys
또는sortedDictionary.Values
그리고 나서 그들 위로 루프합니다.foreach
또한 정렬된 순서대로 진행합니다.그은 그방법들이되때문니다입기오돌아다때니▁return▁those▁this문을 반환하기 때문입니다.System.Collections.Generic.SortedDictionary<TKey,TValue>.KeyCollection
또는SortedDictionary<TKey,TValue>.ValueCollection
원본 사전의 종류를 유지 관리하는 개체입니다.
이 답변은 두 가지 솔루션을 비교하기 위한 것이지 제안된 솔루션이 아닙니다.
다른 답변에서 제안한 것처럼 다른 목록을 만드는 대신 다음을 사용할 수 있습니다.for
사전을사루기하프여하용▁using를 하여 루프하기Count
및 루프정 의경건우조에 대해Keys.ElementAt(i)
열쇠를 가지러 왔습니다.
for (int i = 0; i < dictionary.Count; i++)
{
dictionary[dictionary.Keys.ElementAt(i)] = 0;
}
처음에는 키 목록을 작성할 필요가 없기 때문에 이것이 더 효율적일 것이라고 생각했습니다.테스트를 실행한 후에 나는 그것을 발견했습니다.for
루프 솔루션은 훨씬 덜 효율적입니다.그 이유는ElementAt
의 O(n)입니다.dictionary.Keys
속성. 컬렉션의 처음부터 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
언급URL : https://stackoverflow.com/questions/1070766/editing-dictionary-values-in-a-foreach-loop
'programing' 카테고리의 다른 글
웹에서 호출하면 저장 프로시저가 느리고 Management Studio에서 호출하면 저장 프로시저가 빠름 (0) | 2023.05.23 |
---|---|
소스 제어에 Visual Studio 2015.vs 폴더를 추가해야 합니까? (0) | 2023.05.23 |
특정 분기에 대한 커밋만 가져오려면 로그를 Git합니다. (0) | 2023.05.23 |
Postgre를 변경하는 방법SQL 사용자 암호? (0) | 2023.05.23 |
WPF 창에서 닫기 단추를 숨기는 방법은 무엇입니까? (0) | 2023.05.23 |