Case Insensitive Key Dictionary in C#

Dictionary in c# is a very powerful data structure. You can create a collection of items that can referenced by some key. The key can be of any data type. If you have ever used Dictionary datatype, you would have definitely used a Dictionary with string keys. I know all that, you can say.Why am I reading this tutorial? Spare me for just a few more lines. Let me just give you some context. So that's how you create a dictionary.

var dict = new Dictionary<string, object>();

Using add method, you can add the items as well.

dict.Add("item1", false);
dict.Add("ITEM1", 5);
dict.Add("Item1", true);

It's interesting isn't it. Do you think the above set of statements produce any error? After all keys are'item1' right? Wrong. The issue here is that the string keys in Dictionary are not case sensitive. And so, there won't be any error with above statements.

Enough, I know all that. How can I make it insensitive?

Okay, okay, let's begin.

**Update: As Ceser Perez mentioned in comments, there is a far better way available to create a case insensitive dictionary in C# using one of available overload. I would recommend you to use that overload instead of the method I mentioned below. **so return();

Create a new class that inherits the existing dictionary.

public class CaseInsensitiveDictionary<T>:Dictionary<string, T>
{

}

Because the key is string, we just need the generic type parameter for the value, and so we directly pass the string key in the parent class. Next we need to override a two things. One property and one indexer from the Dictionary class.1. ContainsKey method The methods of Dictionary class are not marked Virtual, so we'll need to use new keyword to override the functionality of this method

public new bool ContainsKey(string key)
{
    if (base.ContainsKey(key))
        return true;
    return !string.IsNullOrEmpty(GetActualKey(key));
}

The method is straightforward. It first checks if the key being searched actually exist is the parent dictionary, if it does, it returns true. Otherwise, we call a function which helps us find the same key in other case. If it exists we return true. Otherwise false it return.2. Add method Similarly, we need to have a new Add method which gets the actual key and then tries to Add the value. If the same key (case insensitive) has already been added, it'll give an argument exception.

public new void Add(string key, TValue value)
{
     key = GetActualKey(key);
     base.Add(key, value);
}

3. The Indexer We also need to override the indexer so that when we tries to access the value by key, it should return appropriate value.

public new TValue this[string key]
{
     get
     {
          key = GetActualKey(key);
          return base[key];
     }
     set
     {
          key = GetActualKey(key);
          base[key] = value;
     }
}

The Magic function Now the magic function which actually helps us doing all this stuff is GetActualKey . It just compares the key in question with the key in dictionary without taking into account the case of the key. Easy no?

private string GetActualKey(string passedKey)
{
     return base.Keys.FirstOrDefault(x => string.Compare(x, passedKey, StringComparison.OrdinalIgnoreCase) == 0);
}

That's it, so the whole class looks like

    /// <summary>
    /// Represents a dictionary where keys are string and case insensitive
    /// </summary>
    public class CaseInsensitiveDictionary<TValue> : Dictionary<string, TValue>
    {
        public new bool ContainsKey(string key)
        {
            if (base.ContainsKey(key))
                return true;

            return !string.IsNullOrEmpty(GetActualKey(key));
        }

        public new TValue this[string key]
        {
            get
            {
                key = GetActualKey(key);
                return base[key];
            }
            set
            {
                key = GetActualKey(key);
                base[key] = value;
            }
        }

        public new void Add(string key, TValue value)
        {
            key = GetActualKey(key);
            base.Add(key, value);
        }
        /// <summary>
        /// Tries to get the key in other case if present
        /// </summary>
        /// <param name="passedKey"></param>
        /// <returns></returns>
        private string GetActualKey(string passedKey)
        {
            return base.Keys.FirstOrDefault(x => string.Compare(x, passedKey, StringComparison.OrdinalIgnoreCase) == 0);
        }
    }

To use this dictionary, you just need to instantiate this class and use it just like the normal dictionary.

var cDict = new CaseInsensitiveDictionary<object>(); //because it's a string key dictionary, we only need one data type in it's definition.

Now if you try to perform these types of operations, you are doomed.

cDict.Add("Item1", false);
cDict.Add("item1", 5); //throws exception
cDict.Add("ITEM1", "ex"); //throws exception

so before you add, you just need to check with ContainsKey method to see if it's there

cDict.Add("Item1", false);
cDict.ContainsKey("item1"); //returns true
cDict.ContainsKey("ITEM1"); //returns true

Cool isn't it?