Incrementing a numerical value in a dictionary

C#Dictionary

C# Problem Overview


I'm using the code below to either increment or insert a value in a dictionary. If the key I'm incrementing doesn't exist I'd like to set its value to 1.

 public void IncrementCount(Dictionary<int, int> someDictionary, int id)
 {  
     int currentCount;
     if (someDictionary.TryGetValue(id, out currentCount))
     {
         someDictionary[id] = currentCount + 1;
     }
     else
     {
         someDictionary[id] = 1;
     }
 }

Is this an appropriate way of doing so?

C# Solutions


Solution 1 - C#

Your code is fine. But here's a way to simplify in a way that doesn't require branching in your code:

int currentCount;

// currentCount will be zero if the key id doesn't exist..
someDictionary.TryGetValue(id, out currentCount); 

someDictionary[id] = currentCount + 1;

This relies on the fact that the TryGetValue method sets value to the default value of its type if the key doesn't exist. In your case, the default value of int is 0, which is exactly what you want.


UPD. Starting from C# 7.0 this snippet can be shortened using out variables:

// declare variable right where it's passed
someDictionary.TryGetValue(id, out var currentCount); 
someDictionary[id] = currentCount + 1;

Solution 2 - C#

As it turns out it made sense to use the ConcurrentDictionary which has the handy upsert method: AddOrUpdate.

So, I just used:

someDictionary.AddOrUpdate(id, 1, (id, count) => count + 1);  

Solution 3 - C#

Here's a nice extension method:

	public static void Increment<T>(this Dictionary<T, int> dictionary, T key)
	{
    	int count;
   		dictionary.TryGetValue(key, out count);
		dictionary[key] = count + 1;
 	}

Usage:

var dictionary = new Dictionary<string, int>();
dictionary.Increment("hello");
dictionary.Increment("hello");
dictionary.Increment("world");

Assert.AreEqual(2, dictionary["hello"]);
Assert.AreEqual(1, dictionary["world"]);

Solution 4 - C#

It is readable and the intent is clear. I think this is fine. No need to invent some smarter or shorter code; if it doesn't keep the intent just as clear as your initial version :-)

That being said, here is a slightly shorter version:

public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{
    if (!someDictionary.ContainsKey(id))
        someDictionary[id] = 0;
    
    someDictionary[id]++;
}

If you have concurrent access to the dictionary, remember to synchronize access to it.

Solution 5 - C#

Just some measurements on .NET 4 for integer keys.

It's not quite an answer to your question, but for the sake of completeness I've measured the behavior of various classes useful for incrementing integers based on integer keys: simple Array, Dictionary (@Ani's approach), Dictionary (simple approach), SortedDictionary (@Ani's approach) and ConcurrentDictionary.TryAddOrUpdate.

Here is the results, adjusted by 2.5 ns for wrapping with classes instead of direct usage:

Array                 2.5 ns/inc
Dictionary (@Ani)    27.5 ns/inc
Dictionary (Simple)  37.4 ns/inc
SortedDictionary    192.5 ns/inc
ConcurrentDictionary 79.7 ns/inc

And that's the code.

Note that ConcurrentDictionary.TryAddOrUpdate is three times slower than Dictionary's TryGetValue + indexer's setter. And the latter is ten times slower than Array.

So I would use an array if I know the range of keys is small and a combined approach otherwise.

Solution 6 - C#

Here is a handy unit test for you to play with concerning ConcurrentDictionary and how to keep the values threadsafe:

     ConcurrentDictionary<string, int> TestDict = new ConcurrentDictionary<string,int>();
     [TestMethod]
     public void WorkingWithConcurrentDictionary()
     {
         //If Test doesn't exist in the dictionary it will be added with a value of 0
         TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);

         //This will increment the test key value by 1 
         TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);
         Assert.IsTrue(TestDict["Test"] == 1);

         //This will increment it again
         TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);
         Assert.IsTrue(TestDict["Test"] == 2);

         //This is a handy way of getting a value from the dictionary in a thread safe manner
         //It would set the Test key to 0 if it didn't already exist in the dictionary
         Assert.IsTrue(TestDict.GetOrAdd("Test", 0) == 2);
         
         //This will decriment the Test Key by one
         TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue-1);
         Assert.IsTrue(TestDict["Test"] == 1);
     }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKingNestorView Question on Stackoverflow
Solution 1 - C#AniView Answer on Stackoverflow
Solution 2 - C#KingNestorView Answer on Stackoverflow
Solution 3 - C#yellowbloodView Answer on Stackoverflow
Solution 4 - C#driisView Answer on Stackoverflow
Solution 5 - C#tsulView Answer on Stackoverflow
Solution 6 - C#MarkWallsView Answer on Stackoverflow