c# - 変更 - foreachループでの辞書値の編集




dictionary foreach c# (8)

.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 |

私は辞書から円グラフを作成しようとしています。 円グラフを表示する前に、データを整理したいと思います。 私は、パイの5%未満であるパイスライスをすべて削除し、それらを「その他の」パイスライスに入れます。 しかし、私はCollection was modified; enumeration operation may not execute Collection was modified; enumeration operation may not execute時に例外を実行し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);

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

foreachループでToList()呼び出します。 この方法では、一時変数コピーは必要ありません。 これはLinqに依存しています。これは.Net 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;
    }
}

あなたが創造的であると感じたら、このようなことをすることができます。 変更を加えるには、辞書を後方にループします。

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

}

確かに同一ではありませんが、とにかく興味があるかもしれません...


この行のコレクションを変更しています:

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

ちょうどちょうどあなたの辞書に対していくつかの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);
}

他の答えに加えて、 sortedDictionary.KeysまたはsortedDictionary.Valuesを取得し、 foreachしてそれらをループすると、ソートされた順序でも実行されることに注意してください。 これらのメソッドは、元の辞書の並べ替えを維持するSystem.Collections.Generic.SortedDictionary<TKey,TValue>.KeyCollectionまたはSortedDictionary<TKey,TValue>.ValueCollectionオブジェクトをSortedDictionary<TKey,TValue>.ValueCollectionです。


免責事項:私はあまりC#

HashTableに格納されているDictionaryEntryオブジェクトを変更しようとしています。 Hashtableは1つのオブジェクト、つまりDictionaryEntryのインスタンスのみを格納します。 キーまたは値を変更するだけで、HashTableが変更され、列挙子が無効になります。

あなたはループの外でそれを行うことができます:

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

最初に変更する値のすべてのキーのリストを作成し、代わりにそのリストを反復処理します。





.net-2.0