Most efficient way to retrieve a KeyValuePair from Dictionary - c#

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

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.

Keep differences between two hashtables

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.

Iterate over C# dictionary's keys with index?

How do I iterate over a Dictionary's keys while maintaining the index of the key.
What I've done is merge a foreach-loop with a local variable i which gets incremented by one for every round of the loop.
Here's my code that works:
public IterateOverMyDict()
{
int i=-1;
foreach (string key in myDict.Keys)
{
i++;
Console.Write(i.ToString() + " : " + key);
}
}
However, it seems really low tech to use a local variable i.
I was wondering if there's a way where I don't have to use the "extra" variable?
Not saying this is a bad way, but is there a better one?
There's no such concept as "the index of the key". You should always treat a Dictionary<TKey, TValue> as having an unpredictable order - where the order which you happen to get when iterating over it may change. (So in theory, you could add one new entry, and the entries could be in a completely different order next time you iterated over them. In theory this could even happen without you changing the data, but that's less likely in normal implementations.)
If you really want to get the numeric index which you happened to observe this time, you could use:
foreach (var x in dictionary.Select((Entry, Index) => new { Entry, Index }))
{
Console.WriteLine("{0}: {1} = {2}", x.Index, x.Entry.Key, x.Entry.Value);
}
... but be aware that that's a fairly misleading display, as it suggests an inherent ordering.
From the documentation:
For purposes of enumeration, each item in the dictionary is treated as a KeyValuePair<TKey, TValue> structure representing a value and its key. The order in which the items are returned is undefined.
EDIT: If you don't like the Select call here, you could create your own extension method:
public struct IndexedValue<T>
{
private readonly T value;
private readonly int index;
public T Value { get { return value; } }
public int Index { get { return index; } }
public IndexedValue(T value, int index)
{
this.value = value;
this.index = index;
}
}
public static class Extensions
{
public static IEnumerable<IndexedValue<T>> WithIndex<T>
(this IEnumerable<T> source)
{
return source.Select((value, index) => new IndexedValue<T>(value, index));
}
}
Then your loop would be:
foreach (var x in dictionary.WithIndex())
{
Console.WriteLine("{0}: {1} = {2}", x.Index, x.Value.Key, x.Value.Value);
}
Technically, the key is the index in a Dictionary<TKey, TValue>. You're not guaranteed to get the items in any specific order, so there's really no numeric index to be applied.
Not really. Note that keys in a dictionary are not logically "ordered". They don't have an index. There is no first or last key, from the Dictionary's point of view. You can keep track on your own whether this is the first key returned by the enumerator, as you are doing, but the Dictionary has no concept of "give me the 5th key", so you couldn't use a for loop with an indexer as you could with a list or array.
Dictionaries are not exactly lists, arrays, or vectors. They take those constructs a step further. The key can be the index:
Dictionary myDictionary<int, string> = new Dictionary<int, string>()
{
{0, "cat"},
{1, "dog"},
{2, "pig"},
{3, "horse"}
};
myDictionary[4] = "hat";
for int i = 0; i <5; i++){
Console.Writeline(myDictionary[i]);
}
At this point you are probably missing most of the benefits of a dictionary (which is similar to enumeration with the benefit of sorting quickly on key values), and using it like a list.
The Select((Entry, Index) => new { Entry, Index }) approach is probably best in the specific context of this question but, as an alternative, System.Linq.Enumerable now lets you convert a dictionary into a list. Something like this would work:
var x = dictionary.ToList();
for (int y=0; y<x.Count; y++) Console.WriteLine(y + " = " + x[y].Key);
There are pros & cons to both approaches depending on what you're trying to do.

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 compare two Dictionary<string,int> to find if any of the value has changed?

I have two dictionary personalizePatientChartDictionary and personalizePatientChartDictionaryOriginal. Both have same key but value can be different. I want to find out if there is any difference in value for a key. I have come up with the following function. Is there any better way of doing this? I am using .NET 4.0.
void CompareOriginalValues()
{
foreach (KeyValuePair<string, int> Origkvp in personalizePatientChartDictionaryOriginal)
{
foreach (KeyValuePair<string, int> kvp in personalizePatientChartDictionary)
{
if ((Origkvp.Key == kvp.Key) && (Origkvp.Value != kvp.Value))
{
hasDictionaryChanged = true;
break;
}
}
if (hasDictionaryChanged)
{
break;
}
}
}
foreach (var kvp in personalizePatientChartDictionaryOriginal)
{
int value;
if (personalizePatientChartDictionary.TryGetValue(kvp.Key, out value))
{
if (kvp.Value != value)
{
hasDictionaryChanged = true;
break;
}
}
}
Note that this code (and your original code) don't detect if there are keys in one dictionary that aren't present in the other. It only checks that the value associated with a particular key in the first dictionary is associated with the same key in the second dictionary, but only if that key actually exists in the second dictionary.
Seems good. But it would be good to return hasDictionaryChanged to indicate that it changed
You can do it using LINQ (don't forget to add "using System.Linq" to your usings)
bool hasDictionaryChanged = personalizePatientChartDictionaryOriginal.Except(personalizePatientChartDictionary).Count() > 0;
For more information about what you can do with LINQ, see 101 LINQ Samples.
It would be possible to write your own version of the dictionary class, inherit from dictionary and IComparable.
Then you could do dictionary1.Compare(dictionary2);
Here is a good resource for the ICompareable interface
http://msdn.microsoft.com/en-us/library/system.icomparable.aspx

Categories