How To Change A Dictionary Key In C#

How to change a dictionary key banner image

Introduction

The challenge with changing the dictionary's key in C# is that it is supposed to be immutable meaning it doesn't change. This leaves us with two other options, that is to remove the existing key and add a new key to the dictionary or to add the new updated key and value into a new dictionary. I will go over both of those methods in this article.

Delete Then Add Method

In this method, we, need to have the existing key already. Once we have the key that we want to change. I will save the value then delete the existing key and add a new key with the previous value. Let's see an example below.

Delete Then Add Method Example Code

In this example, I have a dictionary with a list of cities and their zip codes. I will replace the key for Phoenix with Jacksonville.

Dictionary<string, int> cityZipCodes = new Dictionary<string, int>()
{
    {"New York City", 10001},
    {"Los Angeles", 90001},
    {"Chicago", 60601},
    {"Houston", 77001},
    {"Phoenix", 85001},
    {"Philadelphia", 19101},
    {"San Antonio", 78201},
    {"San Diego", 92101},
    {"Dallas", 75201},
    {"San Jose", 95101}
};

string existingKey = "Phoenix";
string newKey = "Jacksonville";
ChangeDictionaryKey(existingKey, newKey, cityZipCodes);
int count = 1;

foreach (KeyValuePair<string, int> entry in cityZipCodes)
{
    Console.WriteLine($"{count++}. key:{entry.Key}, value:{entry.Value}");//Print screen
}

void ChangeDictionaryKey(string existingKey, string newKey, Dictionary<string, int> dictionary)
{
    if (dictionary.TryGetValue(existingKey, out int existingValue))//Check if the key exists in the dictionary before trying to remove it and get the existing value to save in a temp variable
    {
        dictionary.Remove(existingKey);//Delete the existing key and value
        dictionary.Add(newKey, existingValue);//Add the new key with the previous value into a new key-value pair
    }
}
Code Output
1. key:New York City, value:10001
2. key:Los Angeles, value:90001
3. key:Chicago, value:60601
4. key:Houston, value:77001
5. key:Jacksonville, value:85001
6. key:Philadelphia, value:19101
7. key:San Antonio, value:78201
8. key:San Diego, value:92101
9. key:Dallas, value:75201
10. key:San Jose, value:95101

As expected, you can see that Jacksonville is now on the list in place of Phoenix.

Delete Then Add Method Speed Test

Next, I want to test the speed of this method. It will be a massive test. If I have a list of keys and loop through each key and replace the text with a new text for each of the keys. How fast can this get done if I have 1 test and do this operation 100 times then with 1 million objects in the dictionary. Below is a summary of the test parameters.

Test Parameters
Test ParametersTotal
Tests10
Function Calls Per Test100
Objects Per Function Call1 Million

Delete Then Add Method Speed Test Code

void TestMethod(Dictionary<string, DateTime> dictionary)
{
    foreach (string existingKey in keysList)
    {
        string newKey = existingKey + "0";//random number added to the key to create a new key
        if (dictionary.TryGetValue(existingKey, out DateTime existingValue))//Check if the key exists in the dictionary before trying to remove it and get the existing value to save in a temp variable
        {
            dictionary.Remove(existingKey);//Delete the existing key and value
            dictionary.Add(newKey, existingValue);//Add the new key with the previous value into a new key-value pair
        }
    }
}
Code Output
Test 1:Function Calls:100, In 0m 9s 401ms
Test 2:Function Calls:100, In 0m 8s 786ms
Test 3:Function Calls:100, In 0m 8s 869ms
Test 4:Function Calls:100, In 0m 8s 780ms
Test 5:Function Calls:100, In 0m 10s 437ms
Test 6:Function Calls:100, In 0m 10s 216ms
Test 7:Function Calls:100, In 0m 10s 106ms
Test 8:Function Calls:100, In 0m 9s 724ms
Test 9:Function Calls:100, In 0m 10s 393ms
Test 10:Function Calls:100, In 0m 8s 457ms
Delete Then Add Average Speed:9518ms, In 10 Tests

This is the baseline for this kind of test that it takes about 9.5 seconds to complete. We will try one more method and see how the two compare in performance.

Full Delete Then Add Method Speed Test Code

using System.Diagnostics;

int numberOfTests = 10;//Number of tests 
int numberOfFunctionCalls = 100;//Number of function calls made per test
int numberOfObjectsToCreate = 1000000;//Number test objects
string testName = "Delete Then Add";//Test name to print to average
List<string> keysList = new List<string>();
void TestMethod(Dictionary<string, DateTime> dictionary)
{
    foreach (string existingKey in keysList)
    {
        string newKey = existingKey + "0";//random number added to the key to create a new key
        if (dictionary.TryGetValue(existingKey, out DateTime existingValue))//Check if the key exists in the dictionary before trying to remove it and get existing value to save in a temp variable
        {
            dictionary.Remove(existingKey);//Delete the existing key and value
            dictionary.Add(newKey, existingValue);//Add the new key with the previous value into a new key value pair
        }
    }
}

List<double> testSpeedList = new List<double>();
for (int testIndex = 0; testIndex < numberOfTests; testIndex++)
{
    testSpeedList.Add(StartTest(testIndex));
}
Console.WriteLine($"{testName} Average Speed:{Math.Round(testSpeedList.Average())}ms, In {numberOfTests} Tests");

double StartTest(int testIndex)
{
    Stopwatch stopwatch = new Stopwatch();
    Dictionary<string, DateTime> testData = GetDictionaryData();//Get intial random generated data
    for (int i = 0; i < numberOfFunctionCalls; i++)
    {
        stopwatch.Start();//Start the Stopwatch timer
        TestMethod(testData);//
        stopwatch.Stop();//Stop the Stopwatch timer
    }
    stopwatch.Stop();//Stop the Stopwatch timer
    Console.WriteLine($"Test {testIndex + 1}:Function Calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms");
    return stopwatch.Elapsed.TotalMilliseconds;
}

Dictionary<string, DateTime> GetDictionaryData()
{
    Dictionary<string, DateTime> testData = new Dictionary<string, DateTime>();
    keysList = new List<string>();
    while (numberOfObjectsToCreate > testData.Count)
    {
        DateTime date = GetDateTime();
        string dateString = date.ToShortDateString();
        if (!testData.ContainsKey(dateString))
        {
            testData[dateString] = date;
            keysList.Add(dateString);
        }
    }
    return testData;
}

int GetRandomInt(int minNumber, int maxNumber)
{
    Random random = new Random();//Create Random class
    int randomInt = random.Next(minNumber, maxNumber);//Get a random number between 1 and the maxnumber
    return randomInt;
}

DateTime GetDateTime()
{
    DateTime dateTime = new DateTime(GetRandomInt(1, 6000), GetRandomInt(1, 12), GetRandomInt(1, 29));
    return dateTime;
}

Add To New Dictionary Method

Another way we get around the immutable issue of a dictionary is to add this new key to a new dictionary. I will need to extract the previous value and then add it to the new dictionary.

Add To New Dictionary Method Example Code

Dictionary<string, int> cityZipCodes = new Dictionary<string, int>()
{
    {"New York City", 10001},
    {"Los Angeles", 90001},
    {"Chicago", 60601},
    {"Houston", 77001},
    {"Phoenix", 85001},
    {"Philadelphia", 19101},
    {"San Antonio", 78201},
    {"San Diego", 92101},
    {"Dallas", 75201},
    {"San Jose", 95101}
};

string existingKey = "Phoenix";
string newKey = "Jacksonville";
Dictionary<string, int> newDictionary = new Dictionary<string, int>();
ChangeDictionaryKey(existingKey, newKey, cityZipCodes, newDictionary);
int count = 1;

foreach (KeyValuePair<string, int> entry in cityZipCodes)
{
    Console.WriteLine($"{count++}. key:{entry.Key}, value:{entry.Value}");//Print screen
}
Console.WriteLine($"New Dictionary:");//Print screen

foreach (KeyValuePair<string, int> entry in newDictionary)
{
    Console.WriteLine($"{count++}. key:{entry.Key}, value:{entry.Value}");//Print screen
}

void ChangeDictionaryKey(string existingKey, string newKey, Dictionary<string, int> existingDictionary, Dictionary<string, int> newDictionary)
{
    if (existingDictionary.TryGetValue(existingKey, out int existingValue))//Check if the key exists in the dictionary before trying to remove it and get the existing value to save in a temp variable
    {
        newDictionary.Add(newKey, existingValue);//Add the new key with the previous value into a new key-value pair
    }
}

Code Output
1. key:New York City, value:10001
2. key:Los Angeles, value:90001
3. key:Chicago, value:60601
4. key:Houston, value:77001
5. key:Phoenix, value:85001
6. key:Philadelphia, value:19101
7. key:San Antonio, value:78201
8. key:San Diego, value:92101
9. key:Dallas, value:75201
10. key:San Jose, value:95101
New Dictionary:
11. key:Jacksonville, value:85001

With this, we have a clearer separation between the two dictionaries and their purpose and also have a record of the old and new keys for reference.

Add To New Dictionary Speed Test

This will be the same test as before. Below are the test parameters.

Add To New Dictionary Speed Test Code

void TestMethod(Dictionary<string, DateTime> dictionary)
{
    Dictionary<string, DateTime> newDictionary = new Dictionary<string, DateTime>();
    foreach (string existingKey in keysList)
    {
        string newKey = existingKey + "0";//random number added to the key to create a new key
        if (dictionary.TryGetValue(existingKey, out DateTime existingValue))//Check if the key exists in the dictionary before trying to remove it and get the existing value to save in a temp variable
        {
            newDictionary.Add(newKey, existingValue);//Add the new key with the previous value into a new key-value pair
        }
    }
}
Code Output
Test 1:Function Calls:100, In 0m 25s 26ms
Test 2:Function Calls:100, In 0m 24s 126ms
Test 3:Function Calls:100, In 0m 23s 967ms
Test 4:Function Calls:100, In 0m 24s 786ms
Test 5:Function Calls:100, In 0m 24s 349ms
Test 6:Function Calls:100, In 0m 24s 188ms
Test 7:Function Calls:100, In 0m 23s 673ms
Test 8:Function Calls:100, In 0m 24s 691ms
Test 9:Function Calls:100, In 0m 24s 249ms
Test 10:Function Calls:100, In 0m 24s 489ms
Add To New Dictionary Average Speed:24355ms, In 10 Tests

This takes about 24 seconds and so it is extremely slow and far slower than the previous method.

Full Add To New Dictionary Speed Test Code

using System.Diagnostics;

int numberOfTests = 10;//Number of tests 
int numberOfFunctionCalls = 100;//Number of function calls made per test
int numberOfObjectsToCreate = 1000000;//Number test objects
string testName = "Add To New Dictionary";//Test name to print to average
List<string> keysList = new List<string>();
void TestMethod(Dictionary<string, DateTime> dictionary)
{
    Dictionary<string, DateTime> newDictionary = new Dictionary<string, DateTime>();
    foreach (string existingKey in keysList)
    {
        string newKey = existingKey + "0";//random number added to the key to create a new key
        if (dictionary.TryGetValue(existingKey, out DateTime existingValue))//Check if the key exists in the dictionary before trying to remove it and get existing value to save in a temp variable
        {
            newDictionary.Add(newKey, existingValue);//Add the new key with the previous value into a new key value pair
        }
    }
}


List<double> testSpeedList = new List<double>();
for (int testIndex = 0; testIndex < numberOfTests; testIndex++)
{
    testSpeedList.Add(StartTest(testIndex));
}
Console.WriteLine($"{testName} Average Speed:{Math.Round(testSpeedList.Average())}ms, In {numberOfTests} Tests");

double StartTest(int testIndex)
{
    Stopwatch stopwatch = new Stopwatch();
    Dictionary<string, DateTime> testData = GetDictionaryData();//Get intial random generated data
    for (int i = 0; i < numberOfFunctionCalls; i++)
    {
        stopwatch.Start();//Start the Stopwatch timer
        TestMethod(testData);//
        stopwatch.Stop();//Stop the Stopwatch timer
    }
    stopwatch.Stop();//Stop the Stopwatch timer
    Console.WriteLine($"Test {testIndex + 1}:Function Calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms");
    return stopwatch.Elapsed.TotalMilliseconds;
}

Dictionary<string, DateTime> GetDictionaryData()
{
    Dictionary<string, DateTime> testData = new Dictionary<string, DateTime>();
    keysList = new List<string>();
    while (numberOfObjectsToCreate > testData.Count)
    {
        DateTime date = GetDateTime();
        string dateString = date.ToShortDateString();
        if (!testData.ContainsKey(dateString))
        {
            testData[dateString] = date;
            keysList.Add(dateString);
        }
    }
    return testData;
}

int GetRandomInt(int minNumber, int maxNumber)
{
    Random random = new Random();//Create Random class
    int randomInt = random.Next(minNumber, maxNumber);//Get a random number between 1 and the maxnumber
    return randomInt;
}

DateTime GetDateTime()
{
    DateTime dateTime = new DateTime(GetRandomInt(1, 6000), GetRandomInt(1, 12), GetRandomInt(1, 29));
    return dateTime;
}

Conclusion

Overall RankMethodSpeed
1Delete Then Add9518ms
2Add To New Dictionary10513ms

The best way to change a key is to delete the key-value pair and then add the new key to the existing value. It performs far better than just adding a new key and existing value to a new dictionary. It also keeps things consolidated into one dictionary so you're not keeping track of multiple dictionaries.

Adding the new key and existing value to a new dictionary is extremely slow and I wouldn't recommend doing it. It is better to just delete and add the keys in this case.

Do you know any other ways to change a key in the dictionary that I missed? Let me know in the comments below.

Get Latest Updates