Как перебрать словарь и изменить значения? - PullRequest
64 голосов
/ 14 февраля 2010
Dictionary<string,double> myDict = new Dictionary();
//...
foreach (KeyValuePair<string,double> kvp in myDict)
 {
     kvp.Value = Math.Round(kvp.Value, 3);
}

Я получаю ошибку: «Свойство или индексатор« System.Collections.Generic.KeyValuePair.Value »не может быть назначен - он доступен только для чтения.»
Как я могу перебрать myDict и изменить значения?

Ответы [ 8 ]

97 голосов
/ 14 февраля 2010

Согласно MSDN :

Оператор foreach является оберткой вокруг счетчика, что позволяет только чтение из коллекции, а не пишу ему.

Используйте это:

var dictionary = new Dictionary<string, double>();
// TODO Populate your dictionary here
var keys = new List<string>(dictionary.Keys);
foreach (string key in keys)
{
   dictionary[key] = Math.Round(dictionary[key], 3);
}
33 голосов
/ 24 мая 2012

Для ленивых программистов:

Dictionary<string, double> dictionary = new Dictionary<string, double>();
foreach (var key in dictionary.Keys.ToList())
{
   dictionary[key] = Math.Round(dictionary[key], 3);
}
8 голосов
/ 14 февраля 2010

Вы не должны изменять словарь во время итерации, в противном случае вы получите исключение.

Итак, сначала скопируйте пары ключ-значение во временный список, затем выполните итерацию по этому временному списку, а затем измените свой словарь:

Dictionary<string, double> myDict = new Dictionary<string, double>();

// a few values to play with
myDict["a"] = 2.200001;
myDict["b"] = 77777.3333;
myDict["c"] = 2.3459999999;

// prepare the temp list
List<KeyValuePair<string, double>> list = new List<KeyValuePair<string, double>>(myDict);

// iterate through the list and then change the dictionary object
foreach (KeyValuePair<string, double> kvp in list)
{
    myDict[kvp.Key] = Math.Round(kvp.Value, 3);
}


// print the output
foreach (var pair in myDict)
{
    Console.WriteLine(pair.Key + " = " + pair.Value);
}

// uncomment if needed
// Console.ReadLine();

вывод (на моей машине):

а = 2,2
b = 77777,333
с = 2,346

Примечание : с точки зрения производительности это решение несколько лучше, чем опубликованные в настоящее время решения, поскольку значение уже присвоено ключу, и нет необходимости извлекать его снова из объекта словаря.

2 голосов
/ 06 января 2017

прошло некоторое время, но, может быть, кому-то это интересно:

yourDict = yourDict.ToDictionary(kv => kv.Key, kv => Math.Round(kv.Value, 3))
1 голос
/ 17 августа 2013

Я заметил, что самый быстрый способ (на данный момент) итерации по словарю с модификацией:

//Just a dumb class
class Test<T>
{
    public T value;

    public Test() { }
    public Test(T v) { value = v; }
}

Dictionary<int, Test<object>> dic = new Dictionary<int, Test<object>>();
//Init dictionary
foreach (KeyValuePair<int, Test> pair in dic)
{
    pair.Value.value = TheObject;//Modify
}

VS

List<int> keys = new List<int>(dic.Keys); //This is fast operation   
foreach (int key in keys)
{
    dic[key] = TheObject;
}

Первый занимает около 2,2 с, а второй - 4,5 с (проверенный размер словаря 1000 и повторение 10 000 раз, изменение размера словаря до 10 не изменило соотношения). Также не было особой проблемы с получением списка ключей, значение словаря [ключа] - это просто медленная VS, встроенная в итерацию. Также, если вы хотите еще большую скорость, используйте жестко закодированный тип для «тупого» («Test») класса, с этим я получил около 1,85 с (с жестко закодированным «object»).

EDIT:

Анна уже публиковала такое же решение раньше: https://stackoverflow.com/a/6515474/766304

1 голос
/ 06 октября 2011

Одним из решений было бы заранее поместить ключи в список (или другую коллекцию) и перебирать их при изменении словаря:

Dictionary<string, double> dictionary = new Dictionary<string, double>();

// Populate it
List<string> keys = new List<string>(dictionary.Keys);

foreach (string key in keys)
{
   dictionary[key] = Math.Round(dictionary[key], 3);
}
0 голосов
/ 17 марта 2011

Хотя итерация по словарю напрямую невозможна, поскольку вы получаете исключение (как уже сказал Рон), вам не нужно использовать временный список для решения проблемы.

Вместо этого используйте не foreach, но цикл for для перебора словаря и изменения значений с индексированным доступом:

Dictionary<string, double> myDict = new Dictionary<string,double>();
//...    
for(int i = 0; i < myDict.Count; i++) {
    myDict[myDict.ElementAt(i).Key] = Math.Round(myDict.ElementAt(i).Value, 3);
}
0 голосов
/ 14 февраля 2010

Перебирайте ключи в словаре, а не KeyValuePairs.

Dictionary<string, double> myDict = new Dictionary<string, double>();
//...
foreach (string key in myDict.Keys)
{
    myDict[key] = Math.Round(myDict[key], 3);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...