Editing Dictionary Values in a Foreach Loop

Editing dictionary values in a foreach loop

Setting a value in a dictionary updates its internal "version number" - which invalidates the iterator, and any iterator associated with the keys or values collection.

I do see your point, but at the same time it would be odd if the values collection could change mid-iteration - and for simplicity there's only one version number.

The normal way of fixing this sort of thing is to either copy the collection of keys beforehand and iterate over the copy, or iterate over the original collection but maintain a collection of changes which you'll apply after you've finished iterating.

For example:

Copying keys first

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

Or...

Creating a list of modifications

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

Modifying the contents of a Dictionary in foreach loop

You can simply use ToList on the dictionary to copy each key-value pair into a list. Then iterate over that list instead of Dictionary:

foreach (var entry in id_StreetNameDictionary.ToList())
{
if(something_happens())
{
Corpus_22_04_2014_StreetTable_Row r = entry.Value;
//Modify r
id_StreetNameDictionary[entry.Key] = r;
}
}

How to iterate through Dictionary and change values?

According to MSDN:

The foreach statement is a wrapper
around the enumerator, which allows
only reading from the collection, not
writing to it.

Use this:

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

Update Dictionary Value field through loop

You can't iterate over a Dictionary and change the internal value as it will break the iterator.

you need to copy the collection, and modify the copied version...example below:

Dictionary<string, double> collection = new Dictionary<string, double>();
collection.Add("A", 1.0);
collection.Add("B", 2.0);

Dictionary<string, double> collection2 = new Dictionary<string, double>(collection);

foreach (KeyValuePair<string, double> pair in collection)
{
double val = 3.0;
collection2.Remove(pair.Key);
collection2.Add(pair.Key, val);
}

Modify a dictionary in a for loop in C#

Dictionary<,> itself doesn't provide a good way of doing this - because updating the value associated with a key counts as a change that invalidates any iterator. ConcurrentDictionary<,> does allow this though, and even has an AddOrUpdate method which will help you:

using System;
using System.Linq;
using System.Collections.Concurrent;

class Test
{
static void Main()
{
var dict = new ConcurrentDictionary<int, int>
{
[10] = 15,
[20] = 5,
[30] = 10
};
foreach (var key in dict.Keys)
{
dict.AddOrUpdate(key, 0, (k, v) => v + 1);
}
Console.WriteLine(string.Join("\r\n", dict.Select(kp => $"{kp.Key}={kp.Value}")));
}
}

Is there any way to edit the Values of a Dictionary during iteration?

You could create an array (or a List with .ToList()) from the .Keys in the foreach, something like this:

foreach (string key in dictionary.Keys.ToArray())
{
dictionary[key] += 1;
}

Modify a dictionary which I am iterating through

You can't modify a collection you're iterating over. In this case, a much better solution would be to create a list of entries to remove by iterating over the dictionary, and then iterate over that list, removing the entries from the dictionary:

List<string> removals = new List<string>();                    
DateTime now = DateTime.Now;
foreach(BruteforceEntry be in Entries.Values)
{
if (be.AddedTimeRemove <= now ||
(be.Unbantime <= now && be.Unbantime.Day == DateTime.Now.Day))
{
removals.Add(be.IPAddress);
}
}
foreach (string address in removals)
{
Entries.Remove(address);
}

Note that if you're using .NET 3.5, you could use a LINQ query to express the first part:

List<string> removals = (from be in Entries.Values
where be.AddedTimeRemove <= now ||
(be.Unbantime <= now &&
be.Unbantime.Day == DateTime.Now.Day)
select be.IPAddress).ToList();

Loop through Dictionary

Using Linq it would be as follows:

personDictionary = personDictionary.ToDictionary(e => e.Key, e => e.Value-1);


Related Topics



Leave a reply



Submit