How to update C# hashtable in a loop? - c#

I'm trying to update a hashtable in a loop but getting an error: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
private Hashtable htSettings_m = new Hashtable();
htSettings_m.Add("SizeWidth", "728");
htSettings_m.Add("SizeHeight", "450");
string sKey = "";
string sValue = "";
foreach (DictionaryEntry deEntry in htSettings_m)
{
// Get value from Registry and assign to sValue.
// ...
// Change value in hashtable.
sKey = deEntry.Key.ToString();
htSettings_m[sKey] = sValue;
}
Is there way around it or maybe there is a better data structure for such purpose?

you could read the collection of keys into another IEnumerable instance first, then foreach over that list
System.Collections.Hashtable ht = new System.Collections.Hashtable();
ht.Add("test1", "test2");
ht.Add("test3", "test4");
List<string> keys = new List<string>();
foreach (System.Collections.DictionaryEntry de in ht)
keys.Add(de.Key.ToString());
foreach(string key in keys)
{
ht[key] = DateTime.Now;
Console.WriteLine(ht[key]);
}

In concept I would do:
Hashtable table = new Hashtable(); // ps, I would prefer the generic dictionary..
Hashtable updates = new Hashtable();
foreach (DictionaryEntry entry in table)
{
// logic if something needs to change or nog
if (needsUpdate)
{
updates.Add(key, newValue);
}
}
// now do the actual update
foreach (DictionaryEntry upd in updates)
{
table[upd.Key] = upd.Value;
}

If you're using a Dictionary instead of a Hashtable, so that the type of the keys is known, the easiest way to make a copy of the Keys collection to avoid this exception is:
foreach (string key in new List<string>(dictionary.Keys))
Why are you getting an exception telling you that you've modified the collection you're iterating over, when in fact you haven't?
Internally, the Hashtable class has a version field. The Add, Insert, and Remove methods increment this version. When you create an enumerator on any of the collections that the Hashtable exposes, the enumerator object includes the current version of the Hashtable. The enumerator's MoveNext method checks the enumerator's version against the Hashtable's, and if they're not equal, it throws the InvalidOperationException you're seeing.
This is a very simple mechanism for determining whether or not the Hashtable has been modified. In fact it's a little too simple. The Keys collection really ought to maintain its own version, and its GetEnumerator method ought to save the collection's version in the enumerator, not the Hashtable's version.
There's another, subtler design defect in this approach. The version is an Int32. The UpdateVersion method does no bounds checking. It's therefore possible, if you make exactly the right number of modifications to the Hashtable (2 times Int32.MaxValue, give or take), for the version on the Hashtable and the enumerator to be the same even though you've radically changed the Hashtable since creating the enumerator. So the MoveNext method won't throw the exception even though it should, and you'll get unexpected results.

The simplest way is to copy the keys into a separate collection, then iterate through that instead.
Are you using .NET 3.5? If so, LINQ makes things a little bit easier.

The key part is the ToArray() method
var dictionary = new Dictionary<string, string>();
foreach(var key in dictionary.Keys.ToArray())
{
dictionary[key] = "new value";
}

You cannot change the set of items stored in a collection while you are enumerating over it, since that makes life very difficult for the iterator in most cases. Consider the case where the collection represents a balanced tree, and may well undergo rotations after an insert. The enumerate would have no plausible way of keeping track of what it has seen.
However, if you are just trying to update the value then you can write:
deEntry.Value = sValue
Updating the value here has no impact on the enumerator.

This is how I did it within a dictionary; resets every value in dict to false:
Dictionary<string,bool> dict = new Dictionary<string,bool>();
for (int i = 0; i < dict.Count; i++)
{
string key = dict.ElementAt(i).Key;
dict[key] = false;
}

It depends on why you are looping through the items in the hashtable. But you would probably be able to iterate throught the keys instead. So
foreach (String sKey in htSettings_m.Keys)
{ // Get value from Registry and assign to sValue.
// ...
// Change value in hashtable.
htSettings_m[sKey] = sValue;
}
The other option is to create a new HashTable. Iterate through the first while adding items to the second then replace the original with the new one.
Looping through the keys requires less object allocations though.

List<string> keyList = htSettings_m.Keys.Cast<string>().ToList();
foreach (string key in keyList) {
It is the same as the other answers, but I like the one line to get the keys.

Convert it to an array:
private Hashtable htSettings_m = new Hashtable();
htSettings_m.Add("SizeWidth", "728");
htSettings_m.Add("SizeHeight", "450");
string sKey = "";
string sValue = "";
ArrayList htSettings_ary = new ArrayList(htSettings_m.Keys)
foreach (DictionaryEntry deEntry in htSettings_ary)
{
// Get value from Registry and assign to sValue.
// ...
// Change value in hashtable.
sKey = deEntry.Key.ToString();
htSettings_m[sKey] = sValue;
}

private Hashtable htSettings_m = new Hashtable();
htSettings_m.Add("SizeWidth", "728");
htSettings_m.Add("SizeHeight", "450");
string sValue = "";
foreach (string sKey in htSettings_m.Keys)
{
// Get value from Registry and assign to sValue
// ...
// Change value in hashtable.
htSettings_m[sKey] = sValue;
}

Maybe you can use Hashtable.Keys collection? Enumerating through that might be possible while changing the Hashtable. But it's only a guess...

Related

Dictionary: Property or indexer cannot be assigned to: it is read only

I am trying to change the value of Keys in my dictionary as follows:
//This part loads the data in the iterator
List<Recommendations> iterator = LoadBooks().ToList();
//This part adds the data to a list
List<Recommendations> list = new List<Recommendations>();
foreach (var item in iterator.Take(100))
{
list.Add(item);
}
//This part adds Key and List as key pair value to the Dictionary
if (!SuggestedDictionary.ContainsKey(bkName))
{
SuggestedDictionary.Add(bkName, list);
}
//This part loops over the dictionary contents
for (int i = 0; i < 10; i++)
{
foreach (var entry in SuggestedDictionary)
{
rec.Add(new Recommendations() { bookName = entry.Key, Rate = CalculateScore(bkName, entry.Key) });
entry.Key = entry.Value[i];
}
}
But it says "Property or Indexer KeyValuePair>.Key Cannot be assigned to. Is read only. What I exactly want to do is change the value of dictionary Key here and assign it another value.
The only way to do this will be to remove and re-add the dictionary item.
Why? It's because a dictionary works on a process called chaining and buckets (it's similar to a hash table with different collision resolution strategy).
When an item is added to a dictionary, it is added to the bucket that its key hashes to and, if there's already an instance there, it's prepended to a chained list. If you were to change the key, it will need to to go through the process of working out where it belongs. So the easiest and most sane solution is to just remove and re-add the item.
Solution
var data = SomeFunkyDictionary[key];
SomeFunkyDictionary.Remove(key);
SomeFunkyDictionary.Add(newKey,data);
Or make your self an extension method
public static class Extensions
{
public static void ReplaceKey<T, U>(this Dictionary<T, U> source, T key, T newKey)
{
if(!source.TryGetValue(key, out var value))
throw new ArgumentException("Key does not exist", nameof(key));
source.Remove(key);
source.Add(newKey, value);
}
}
Usage
SomeFunkyDictionary.ReplaceKey(oldKye,newKey);
Side Note : Adding and removing from a dictionary incurs a penalty; if you don't need fast lookups, it may just be more suitable not use a dictionary at all, or use some other strategy.

c# Dictionary<string,string> how to loop through items without knowing key [duplicate]

This question already has answers here:
How to iterate over a dictionary?
(29 answers)
Closed 8 years ago.
I have a:
var selectedDates = new Dictionary<string, string>();
selectedDates.Add("2014-06-21", DateTime.Now.ToLongDateString());
selectedDates.Add("2014-07-21", DateTime.Now.AddDays(5).ToLongDateString());
selectedDates.Add("2014-08-21", DateTime.Now.AddDays(9).ToLongDateString());
selectedDates.Add("2014-09-21", DateTime.Now.AddDays(14).ToLongDateString());
How can I loop trough items without knowing the key?
For example I want to get the value of the item[0]
If I do:
var item = selectedDates[0].value; // I get an error
How can I loop trough items without knowing the key?
For example I want to get the value of the item[0]
You want to treat the dictionary as (ordered) collection similar to a list or array and get the first item in it?
You can because a Dictionary<string, string> is an IEnumerable<KeyValuePair<string, string>> implicitly. Just use First or FirstOrDefault:
string valueAtFirstPosition = selectedDates.First().Value;
However, note that a dictionary is not meant to be used as as an ordered collection. It is a collection which can be used to fast-lookup a value by a key. But you can enumerate it anyway.
foreach(KeyValuePair<string, string>keyVal in selectedDates)
{
Console.WriteLine("Key: {0} Value: {1}", keyVal.Key, keyVal.Value);
}
You should simply not rely on that order. I think in the current implementation the order is stable as long as you don't delete items. Read
Read: Why is a Dictionary “not ordered”?
try this
foreach (string key in selectedDates.Keys)
{
var item = selectedDates[key];
}
It's simple, loop trough it with a foreach or to get a specific index do:
var date = selectedDates.ElementAt(0).Value;
Let me put together two things for you. Firstly, you can loop or use LINQ to access elements, just as you could do it in a list as well:
var dict = new Dictionary<string, string>();
// loop
foreach (var item in dict)
{
var key = item.Key;
var value = item.Value;
}
// "first" (see below)
var firstItem = dict.First();
However, be aware that what you're referring to as the first item can be pretty much any item in the Dictionary. Dictionaries store elements in any order that is convenient for a lookup (so do sets).
This order is known for some implementations, but lists or arrays might fit better when the order of the elements is important. A Dictionary in .NET is an implementation of a hash table data structure (tree map is another map implementation).
try this :
foreach(var key in selectedDates.Keys)
{
var value = selectedDates[key];
}
Use this overload of Where:
var result = selectedDates.Where((d,i)=>i==0);
Try:
foreach (var date in selectedDates)
{
var item = date.Value;
}

What type is the best for loose numerically-indexed lists in C#?

What I need is something like an array but letting me to assign an element to whatever an index at any time and check if there is already a value assigned to particular index approximately like
MyArray<string> a = new MyArray<string>();
a[10] = "ten";
bool isTheFifthElementDefined = a[5] != null; // false
Perhaps Dictionary<int, string> with its ContainsKey method could do, but isn't there a more appropriate data structure if I want an ordered collection with numeric keys only?
I am also going to need to iterate through the defined elements (with foreach or linq preferably) accessing both the value and the key of current element.
As you mentioned Dictionary seems more appropriate for this.But you can do it with generic lists,for example, when you are creating your list you can specify an element count,and you can give a default temporary value for all your elements.
List<string> myList = new List<string>(Enumerable.Repeat("",5000));
myList[2300] = "bla bla bla..";
For int:
List<int> myList = new List<int>(Enumerable.Repeat(0,5000));
For custom type:
List<MyClass> myList = new List<MyClass>(Enumerable.Repeat(new MyClass(), 100));
Ofcourse It is not the best solution...
Note: Also you can use SortedList instead of Dictionary if you want an ordered collection by keys:
SortedList<TKey, TValue> : Represents a collection of key/value pairs that are sorted by key based on the associated IComparer implementation.
If you need key/value pairs you cannot use a list, you'll need a Dictionary.
The implementation is pretty snappy so don't be too afraid about performance (as long as you don't put too much values in it).
You can iterate over it with
foreach(KeyValuePair<int, string> kvp in dict)
{
}
If you need to order it you can use a list:
List<int> ordered = new List(dict.Keys);
ordered.Sort();
foreach(int key in ordered)
{
}

Get a vaule by key from nested hashtable

I have a nested Hashtable that looks like this.
Hashtable table = new Hashtable();
Hashtable subtable = new Hashtable();
Hashtable options = new Hashtable();
options.Add("file","foo");
subtable.Add("post_option",options);
table.Add(0,subtable);
//foreach here
This is what I have to work with, and I can't get any father up the chain to change what it is. So what I need to be able to do is get "foo" by calling for the key "file" starting from the "table" hashtable. I have tried every combo of foreach and .Keys and .Values. I just can't seem to get it lol. Thank you
Enumerator of Hashtable works similar to enumerator of IDictionary<TKey,TValue>, but it is not generic as it is ancient API came from .NET 1 where generics do not exist. So if you want to iterate over Hashtable with foreach you need to specify the type of item. In case of Hashtable it is DictionaryEntry.
foreach(DictionaryEntry tableEntry in table)
{
// your logic
}
If you do not know the keys of the first two tables, then you need to do something like this.
foreach(DictionaryEntry tableEntry in table)
{
Hashtable subtable = tableEntry.Value as Hashtable;
if (subtable == null)
continue;
foreach(DictionaryEntry subtableEntry in subtable)
{
Hashtable options = subtableEntry.Value as Hashtable;
if (options == null)
continue;
object file = options["file"];
}
}
You just need to do:
object foo = table[0]["post_option"]["file"];

Loop in Dictionary

I use this:
foreach(KeyValuePair<String,String> entry in MyDic)
{
// do something with entry.Value or entry.Key
}
The problem is that I can't change the value of entry.Value or entry.Key
My question is that how can i change the value or key when looping through a dictionary?
And, does dictionary allow duplicated key? And if yes, how can we avoid ?
Thank you
You cannot change the value of a dictionary entry while looping through the items in the dictionary, although you can modify a property on the value if it's an instance of a reference type.
For example,
public class MyClass
{
public int SomeNumber { get; set;}
}
foreach(KeyValuePair<string, MyClass> entry in myDict)
{
entry.Value.SomeNumber = 3; // is okay
myDict[entry.Key] = new MyClass(); // is not okay
}
Trying to modify a dictionary (or any collection) while looping through its elements will result in an InvalidOperationException saying the collection was modified.
To answer your specific questions,
My question is that how can i change the value or key when looping through a dictionary?
The approach to both will be pretty much the same. You can either loop over a copy of the dictionary as Anthony Pengram said in his answer, or you can loop once through all the items to figure out which ones you need to modify and then loop again through a list of those items:
List<string> keysToChange = new List<string>();
foreach(KeyValuePair<string, string> entry in myDict)
{
if(...) // some check to see if it's an item you want to act on
{
keysToChange.Add(entry.Key);
}
}
foreach(string key in keysToChange)
{
myDict[key] = "new value";
// or "rename" a key
myDict["new key"] = myDict[key];
myDict.Remove(key);
}
And, does dictionary allow duplicated key? And if yes, how can we avoid ?
A dictionary does not allow duplicate keys. If you want a collection of <string, string> pairs that does, check out NameValueCollection.
Updating the dictionary in the loop is going to be a problem, as you cannot modify the dictionary as it is being enumerated. However, you can work around this pretty easily by converting the dictionary to a list of KeyValuePair<> objects. You enumerate that list, and then you can modify the dictionary.
foreach (var pair in dictionary.ToList())
{
// to update the value
dictionary[pair.Key] = "Some New Value";
// or to change the key => remove it and add something new
dictionary.Remove(pair.Key);
dictionary.Add("Some New Key", pair.Value);
}
For the second part, the key in a dictionary must be unique.
KeyValuePair's Key and value are read only. But you can change a value like that:
dictionary[key].Value = newValue;
But if you want to change the key, you will have to remove/add a key.
And no, a Dictionary does not allow duplicate keys, it will throw an ArgumentException.
You cannot modify keys while enumerating them.
One method I use for changes to the collection while enumerating them is that I do break; out of the foreach loop when a match is found and item is modified, and am restarting the whole enumeration all over again. That's one way of handling it...
No, Dictionary can't have duplicate keys. If you want something that will sort by key and allow duplicates, you should use some other data structure.
You can do this like
for (int i = 0; i < MyDic.Count; i++)
{
KeyValuePair<string, string> s = MyDic.ElementAt(i);
MyDic.Remove(s.Key);
MyDic.Add(s.Key, "NewValue");
}
And Dictionary doesn't allow duplicates

Categories