Keep differences between two hashtables - c#

I am making a function that compares two hashtables and I want to keep the difference of these tables. So if they both contain 100 keys and only 2 have been altered I want a new hashtable to equal only those 2 differences.
Here is what I have. I really am lost on how to do the (keep differences)
private Hashtable CompareHashtables(Hashtable ht1, Hashtable ht2)
{
Hashtable ResultsOfCompare = new Hashtable();
foreach (DictionaryEntry entry in ht1)
{
if (ht2.ContainsKey(entry.Key) && ht2.ContainsValue(entry.Value) == false)
{
//Keep differences
}
}
return ResultsOfCompare;
}

It seems like you want to check both the key and the value for equality. If both don't match then it's considered a difference. This creates a bit of a problem for you because that type of difference can't be represented in a hash table. Consider the following
ht1: key = "bob" value = 42
ht2: key = "bob" value = 13
Here the key is the same but the value is difference. To store every difference the resulting structure would need to be able to contain 2 different values for the same key. That's not really possible with Hashtable. An ArrayList of the entries that differ may be a better choice for this exercise.
Really there are 3 cases to consider
Both tables contain the same key but have different values
Left table has the key but not right
Right table has the key but not left
Items 2 and 3 can be collapsed together but it's harder to collapse 1 into this bag. You probably need another data structure to instruct you about the difference in the table
struct Difference {
internal readonly bool IsValueDifferent;
internal readonly object Key;
internal readonly object Value;
internal readonly object OtherValue;
internal Difference(object key, object value) {
Key = key;
Value = value;
IsValueDifferent = false;
}
internal Difference(object key, object value, object otherValue) {
Key = key;
Value = value;
OtherValue = otherValue;
IsValueDifferent = true;
}
}
With this you can represent the cases.
private Hashtable CompareHashtables(Hashtable ht1, Hashtable ht2) {
ArrayList diffList = new ArrayList();
foreach (DictionaryEntry entry in ht1) {
object value = ht2[entry.Key];
if (value == null) {
diffList.Add(new Difference(entry.Key, entry.Value));
} else if (!value.Equals(entry.Value)) {
diffList.Add(new Difference(entry.Key, entry.Value, value));
}
}
foreach (DictionaryEntry entry in ht2) {
object value = ht1[entry.Key];
if (value == null) {
diffList.Add(new Difference(entry.Key, entry.Value));
}
}
return diffList;
}
Note: Hashtable and ArrayList are essentially deprecated at this point. Why not use Dictionary<TKey, TValue> and List<T> instead?

If both hashtables contain the key and value either
Remove the value from one of the hashtables you wish to keep as the result (this way you will remove all duplicates). This will only use 2 hashtables but it will also ruin the data from one of the tables.
Store every negative evaluation of this requirement in a third hashtable you will use as result. This will use a total of 3 hashtables but will not remove the original data.
Example using method 2:
private Hashtable CompareHashtables(Hashtable ht1, Hashtable ht2){
Hashtable resultsOfCompare = new Hashtable();
foreach (DictionaryEntry entry in ht1) {
if (!(ht2.ContainsKey(entry.Key) && ht2.ContainsValue(entry.Value))) {
resultsOfCompare.Add(entry.Key, entry.Value);
}
}
return resultsOfCompare;
}
Edit:
as another answer remarked: you cannot store the same key twice in a resulting hashtable. I was under the impression that you would store the data of one hashtable into the resulting one, which means you can still use my method.
However if you want to keep both entries then you will have to look for a different structure.

Related

Most efficient way to retrieve a KeyValuePair from Dictionary

In my app I have a Dictionary<ContainerControl, int>.
I need to check if a key is present in the dictionary and alter its corresponding value if key is found or add the key if not already present.
The key for my dictionary is a ControlContainer object.
I could use this method:
var dict = new Dictionary<ContainerControl, int>();
/*...*/
var c = GetControl();
if (dict.ContainsKey(c))
{
dict[c] = dict[c] + 1;
}
else
{
dict.Add(c, 0);
}
but I think that this way if the key is already present, my dictionary is iterated three times: once in ContainsKey and twice in the if branch.
I wander if there is a more efficient way to do this, something like
var dict = new Dictionary<ContainerControl, int>();
/*...*/
var c = GetControl();
var kvp = dict.GetKeyValuePair(c); /* there is no such function in Dictionary */
if (kvp != null)
{
kvp.Value++;
}
else
{
dict.Add(c, 0);
}
This is possible using linq:
var kvp = dict.SingleOrDefault(x => x.Key == c);
but what about performance?
As noted in comments, finding a key in a dictionary doesn't mean iterating over the whole dictionary. But in some cases it's still worth trying to reduce the lookups.
KeyValuePair<,> is a struct anyway, so if GetKeyValuePair did exist, your kvp.Value++ wouldn't compile (as Value is read-only) and wouldn't work even if it did (as the pair wouldn't be the "original" in the dictionary).
You can use TryGetValue to reduce this to a single "read" operation and a single "write" operation:
// value will be 0 if TryGetValue returns false
if (dict.TryGetValue(c, out var value))
{
value++;
}
dict[c] = value;
Or change to ConcurrentDictionary and use AddOrUpdate to perform the change in a single call.
You could also store a reference type in the dict. This means an extra allocation when you insert an item, but you can mutate items without another dictionary access. You'll need a profiler to tell you whether this is a net improvement!
class IntBox
{
public int Value { get; set; }
}
if (dict.TryGetValue(c, out var box))
{
box.Value++;
}
else
{
dict[c] = new IntBox();
}
With .NET 6 you can use CollectionsMarshal.GetValueRefOrAddDefault for a single lookup:
ref int value = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, c, out bool exists);
if(exists) value++; // changes the value in the dictionary even if it's a value type
Demo: https://dotnetfiddle.net/tnW9P5

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.

Dictionary ContainsKey does not seem to work with string[] key

I am trying to have a data structure with multiple string keys. To do this, I tried to create a Dictionary with string[] element. But the ContainsKey do no seem to work as I expect:
Dictionary<string[], int> aaa = new Dictionary<string[], int>();
int aaaCount = 0;
aaa.Add(new string[] { string1, string2 }, aaaCount++);
if (!aaa.ContainsKey(new string[] { string1, string2 }))
{
aaa.Add(new string[] { string1, string2 }, aaaCount++);
}
I see that there are two entries in aaa after the execution of the code above while I was expecting only one. Is this the expected behaviour? How can I ensure that there are no duplicate entries in the Dictionary?
Note: I tried the same with a list as well (List and the result is the same - the Contains method does not really work with string[])
If you want to use string[] as TKey, you should pass IEqualityComparer<string[]> to the constructor of Dictionary. Because Otherwise Dictionary uses standard comparison for TKey and in case of string[] it just compares references hence string[] is reference type. You have to implement IEqualityComparer yourself. It can be done in the following way:
(The implementation is quite naive, I provide it just as the starting point)
public class StringArrayComparer : IEqualityComparer<string[]>
{
public bool Equals(string[] left, string[] right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if ((left == null) || (right == null))
{
return false;
}
return left.SequenceEqual(right);
}
public int GetHashCode(string[] obj)
{
return obj.Aggregate(17, (res, item) => unchecked(res * 23 + item.GetHashCode()));
}
}
You need to create an IEqualityComparer<string[]> and pass it to the dictionary's constructor.
This tells the dictionary how to compare keys.
By default, it compares them by reference.
Because an array is a reference type, i.e., you are checking reference (identity) equality, not equality based on the values within the array. When you create a new array with the same values the arrays themselves are still two distinct objects, so ContainsKey returns false.
Using an array as a Dictionary key is a bit... odd. What are you trying to map here? There is probably a better way to do it.
You may be better off, if your application supports it, to combine the string array into a single string.
We have numerous cases where two pieces of information uniquely identifies a record in a collection and in these cases, we join the two strings using a value that should never be in either string (i.e. Char(1)).
Since it is usually a class instance that is being added, we let the class specify the generation of the key so that the code adding to the collection only has to worry about checking a single property (i.e. CollectionKey).

Retrieving the key of a value from a hash table c#

I have a hash table that contains values of a^j. j is the key and a^j is the value.
I am now calculating another value a^m. I basically want to see if a^m is in the hash table.
I used the ContainsValue fn. to find the value. How would i go about finding out the key of the value?
Here is a little snippet of where i want to implement the search for the value.
Dictionary<BigInteger, BigInteger> b = new Dictionary<BigInteger, BigInteger>();
***add a bunch of BigIntegers into b***
for(int j=0; j < n; j++)
{
z = q* BigInteger.ModPow(temp,j,mod);
***I want to implement to search for z in b here****
}
Does this change anything? the fact that i am searching while inside a for loop?
The fastest way is probably to iterate through the hashtable's DictionaryEntry items to find the value, which in turn gives you the key. I don't see how else to do it.
Firstly, you should absolutely be using Dictionary<TKey, TValue> instead of Hashtable - if you're using BigInteger from .NET 4, there's no reason not to use generic collections everywhere you can. Chances are for the most part you'd see no difference in how it's used - just create it with:
Dictionary<BigInteger, BigInteger> map =
new Dictionary<BigInteger, BigInteger>();
to start with. One thing to watch out for is that the indexer will throw an exception if the key isn't present in the map - use TryGetValue to fetch the value if it exists and a bool to say whether or not it did exist.
As for finding the key by value - there's no way to do that efficiently from a Dictionary. You can search all the entries, which is most easily done with LINQ:
var key = map.Where(pair => pair.Value == value)
.Select(pair => pair.Key)
.First();
but that will iterate over the whole dictionary until it finds a match, so it's an O(n) operation.
If you want to do this efficiently, you should keep two dictionaries - one from a to a^j and one from a^j to a. When you add an entry, add it both ways round. Somewhere on Stack Overflow I've got some sample code of a class which does this for you, but I doubt I'd be able to find it easily. EDIT: There's one which copes with multiple mappings here; the "single mapping" version is in the answer beneath that one.
Anyway, once you've got two dictionaries, one in each direction, it's easy - obviously you'd just lookup a^m as a key in the second dictionary to find the original value which created it.
Note that you'll need to consider whether it's possible for two original keys to end up with the same value - at that point you obviously wouldn't be able to have both mappings in one reverse dictionary (unless it was a Dictionary<BigInteger, List<BigInteger>> or something similar).
Edit: Changed to use Dictionary<TKey, TValue>
Dictionary<TKey, TValue> is an IEnumerable<KeyValuePair<TKey, TValue>>. If you do a foreach over it directly, you can get both the key and value for each entry.
class SomeType
{
public int SomeData = 5;
public override string ToString()
{
return SomeData.ToString();
}
}
// ...
var blah = new Dictionary<string, SomeType>();
blah.Add("test", new SomeType() { SomeData = 6 });
foreach (KeyValuePair<string, SomeType> item in blah)
{
if(e.Value.SomeData == 6)
{
Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}
}
If you have a newer version of the .Net framework, you could use Linq to find your matches, and place them in their own collection. Here's a code sample showing a little bit of Linq syntax:
using System;
using System.Collections;
using System.Linq;
class SomeType
{
public int SomeData = 5;
public override string ToString()
{
return SomeData.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var blah = new Dictionary<string, SomeType>();
blah.Add("test", new SomeType() { SomeData = 6 });
// Build an enumeration of just matches:
var entriesThatMatchValue = blah
.Where(e => e.Value.SomeData == 6);
foreach (KeyValuePair<string, SomeType> item in entriesThatMatchValue)
{
Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}
// or: ...
// Build a sub-enumeration of just keys from matches:
var keysThatMatchValue = entriesThatMatchValue.Select(e => e.Key);
// Build a list of keys from matches in-line, using method chaining:
List<string> matchingKeys = blah
.Where(e => e.Value.SomeData == 6)
.Select(e => e.Key)
.ToList();
}
}
private object GetKeyByValue(object searchValue)
{
foreach (DictionaryEntry entry in myHashTable)
{
if (entry.Value.Equals(searchValue))
{
return entry.Key;
}
}
return null;
}

How to update C# hashtable in a loop?

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

Categories