76 votes

Comment itérer dans le dictionnaire et changer les valeurs ?

Dictionary<string,double> myDict = new Dictionary();
//...
foreach (KeyValuePair<string,double> kvp in myDict)
 {
     kvp.Value = Math.Round(kvp.Value, 3);
}

Je reçois une erreur : "La propriété ou l'indexeur 'System.Collections.Generic.KeyValuePair.Value' ne peut pas être assigné à -- il est en lecture seule."
Comment puis-je itérer à travers myDict et changer les valeurs ?

1 votes

Si l'ajout d'un grand nombre d'objets de référence est acceptable du point de vue des performances (indirection et plus de pression du gc) et vous avez le contrôle du dictionnaire, alors la création d'une boîte pour vos valeurs résoudrait le problème, par exemple class Box<T> { public T boxed; } et ensuite utiliser un Dictionary<string, Box<double>> que vous pouvez ensuite "modifier" dans le foreach, par exemple : kvp.Value.boxed = 123.456; . Je ne dis pas que c'est la "meilleure" approche, mais elle a sa part d'utilité.

0 votes

112voto

Justin R. Points 10122

Selon MSDN :

L'instruction foreach est une enveloppe autour de l'énumérateur, qui permet seulement la lecture de la collection, et non d'y écrire.

Utilisez ça :

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

7 votes

J'ai testé votre exemple dans .NET 2 et 3.5 et le message "Collection was modified exception" est affiché. Voir : stackoverflow.com/questions/1562729/ Cela a-t-il changé dans .NET 4 ou n'avez-vous pas testé votre exemple ?

3 votes

C'est embarrassant - j'ai omis la partie où la liste est remplie. C'est maintenant corrigé. L'idée ici est que vous pouvez changer la valeur de l'entrée du dictionnaire, mais pas sa référence.

0 votes

J'aurais aimé que vous gardiez la ligne de commentaire de la question avec "//.... (Remplir)". Au premier coup d'oeil, la liste est vide.

40voto

NielsSchroyen Points 71

Pour les programmeurs paresseux :

Dictionary<string, double> dictionary = new Dictionary<string, double>();
foreach (var key in dictionary.Keys.ToList())
{
   dictionary[key] = Math.Round(dictionary[key], 3);
}

2 votes

Comme je codifie maintenant avec .NET 4.5, la fonction ToList() n'est pas disponible, mais la méthode Keys est itérable, donc .ToList() n'est pas nécessaire.

4 votes

@MikeC voir le commentaire sur stackoverflow.com/a/2260462/1037948 -- vous ne pouvez pas directement énumérer sur les clés, les .ToList() est un "hack" pour contourner cela

3 votes

ToList() est une méthode d'extension LINQ. Elle devrait être disponible si vous ajoutez "using System.Linq ;" à vos instructions using.

8voto

Ron Klein Points 3409

Vous ne devez pas modifier le dictionnaire pendant son itération, sinon vous obtenez une exception.

Il faut donc d'abord copier les paires clé-valeur dans une liste temporaire, puis itérer dans cette liste temporaire et enfin modifier votre dictionnaire :

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

(sur ma machine) :

a = 2.2
b = 77777.333
c = 2.346

Note En termes de performances, cette solution est un peu meilleure que les solutions actuellement affichées, puisque la valeur est déjà associée à la clé et qu'il n'est pas nécessaire de la récupérer à nouveau dans l'objet dictionnaire.

1 votes

C'est probablement l'approche que je vais suivre, mais j'aimerais bien connaître les frais généraux qu'entraîne la copie du dictionnaire complet.

2voto

user766304 Points 36

J'ai remarqué que la façon la plus rapide (pour le moment) d'itérer sur un dictionnaire avec modify est :

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

Le premier prend environ 2,2 secondes et le second 4,5 secondes (la taille du dictionnaire testé était de 1000 et a été répété 10 000 fois, le fait de changer la taille du dictionnaire à 10 n'a pas changé les ratios). De plus, il n'y a pas eu de problème avec l'obtention de la liste des clés, l'obtention de la valeur de dictionnaire[clé] est juste lente VS construite en itération. Si vous voulez encore plus de vitesse, utilisez le type codé en dur vers la classe muette ("Test"), avec cela j'ai obtenu environ 1.85s (avec le type codé en dur vers "object").

EDIT :

Anna a déjà publié la même solution : https://stackoverflow.com/a/6515474/766304

1voto

Hypno Points 11

Une solution serait de placer les clés dans une liste (ou une autre collection) au préalable et d'itérer à travers elles tout en modifiant le dictionnaire :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X