4

If I have a Dictionary<char,...> is it possible to make method ContainsKey case-insensitive?

I know, in Dictionary<string,...> is used StringComparer.InvariantCultureIgnoreCase, but what to do when key in dictionary is char-type?

Lenka
  • 95
  • 2
  • 7
  • A *very* simple option would be to add the same value for 'a' and 'A', for example. But consider that as a workaround. – Dion V. Dec 18 '14 at 08:36
  • Reference for your 'I Know ..' : http://stackoverflow.com/questions/13988643/case-insensitive-dictionary-with-string-key-type-in-c-sharp – Pleun Dec 18 '14 at 08:37
  • To make it clear - are you looking for a solution for a case-insensitive dictionary (so `myDict['a']` is equivalent to `myDict['A']`), or *only* a case-insensitive ContainsKey method (so `myDict.ContainsKey('a')` returns true regardless of whether 'a' or 'A', or both, are defined?) – Avner Shahar-Kashtan Dec 18 '14 at 08:43
  • @MickyDuncan Indeed, which is why I did not post it as an aswer. However, it is simple and easy to implement. And after all, it would only take up 26 more spots *if* they are all used - not that much! – Dion V. Dec 18 '14 at 08:47
  • @Avner Shahar-Kashtan I have dictionary with only uppercase letters but when I use ContainsKey('a') it must work. – Lenka Dec 18 '14 at 08:48
  • And if you use `myDict['a']` to retrieve the value, do you need that to work as well? – Avner Shahar-Kashtan Dec 18 '14 at 08:51
  • @MickyDuncan Well, only a-z have uppercases, right? ^^ – Dion V. Dec 18 '14 at 08:54
  • @Avner Shahar-Kashtan Yes, but what I need is to have dictionary with only uppercase letters (efficient memory). With ContainsKey method sometimes I try to check lowercase and sometimes uppercase letter. – Lenka Dec 18 '14 at 08:56
  • @DionV. No I misread question, I thought it was Dictionary<`string`>. :P –  Dec 18 '14 at 08:56

6 Answers6

11

The Dictionary class has a constructor that takes any IEqualityComparer. What you need to do is implement a simple case-insensitive IEqualityComparer<char> and pass it to the constructor, and it will be used when evaluating the key.

This is a similar question for implementing IComparer<char> without case sensitivity. IEqualityComparer will be practically identical:

Community
  • 1
  • 1
Avner Shahar-Kashtan
  • 14,492
  • 3
  • 37
  • 63
8

You can implement your own comparer:

public class CharComparer : IEqualityComparer<char>
{
     public bool Equals(char c1, char c2)
     {
          return char.ToLowerInvariant(c1) == char.ToLowerInvariant(c2);
     }
     public int GetHashCode(char c1)
     {
          return char.ToLowerInvariant(c1).GetHashCode();
     }

}

And pass it to the constructor:

var dict = new Dictioanry<char, object>(new CharComparer());
Selman Genç
  • 100,147
  • 13
  • 119
  • 184
2

You could implement a extension method aswell:

public static bool ContainsKeyInsensitive(this Dictionary<char, ...> dict, char c)
{      
  return dict.ContainsKey(char.ToUpper(c)) || dict.ContainsKey(char.ToLower(c));
}
DerApe
  • 3,097
  • 2
  • 35
  • 55
1

You could also call it twice.

if (dict.ContainsKey(char.ToLowerInvariant(ch)) || dict.ContainsKey(char.ToUpperInvariant(ch)))
{
    ...
}

As an extension method, this becomes:

public static bool ContainsKeyInsensitive<T>(this Dictionary<char, T> dict, char ch)
{
    return dict.ContainsKey(char.ToLowerInvariant(ch)) || dictionary.ContainsKey(char.ToUpperInvariant(ch));
}
Tim Cooke
  • 862
  • 7
  • 14
0

At first; I know the accepted answer works and it is a good answer. Yet, I found out that it is not neccessary to implement any extension method/additional logic to achieve the desired result.

I suppose you are using a dictionary because you want to map a key to a specific value - which is exactly what they are for. If you use it only to look up keys, you should be using a HashSet<char>.

Alright, that being said, let's take a look at the three options you have with char;

  • You are willing to map uppercase as well as lowercase, giving both uppercase and lowercase a different value.
  • You are willing to map only lowercase, meaning uppercase has the same value as uppercase.
  • You are willing to map only uppercase, meaning lowercase has the same value as uppercase.

I'll explain them all three. For all options, consider not knowing whether the input char is uppercase or lowercase.

Option 1; Different keys, different values.

Mapping both is the way to go here. If you need a different value for 'a' and 'A', you need to add them both anyway. Therefore, you can just use .ContainsKey() and it will return the desired result. This option probably doesn't fit your needs, since you are looking for a case insensitive dictionary.

Option 2: Lowercase has the same value as uppercase.

If you are wanting to get the lowercase value anytime, just call .ContainsKey(yourChar.ToLower()).

Option 3: Uppercase has the same value as lowercase.

Same story! Only now, use .ContainsKey(yourChar.ToUpper()) to get the correct results. Since you posted in your comment that your dictionary has uppercased chars only, this should be the way to go.

The same goes for a case insensitive Dictionary<string,T>; just only map either lower or uppercase (lowercase has my preference, but that's your own choice).

The following code snippet should demonstrate how it works.

Dictionary<char, object> dic = new Dictionary<char, object>();

public object GetIfKeyExists(char c)
{
    // Consider c = 'a' or c = 'A'

    // Option 1: Both have different values, so they should be existing anyway.
    if (dic.ContainsKey(c)) return dic[c];

    // Option 2: Only lowercase is mapped.
    char correctInput = c.ToLower();
    if (dic.ContainsKey(correctInput)) return dic[correctInput];

    // Option 3: Only uppercase is mapped.
    char correctInput = c.ToUpper();
    if (dic.ContainsKey(correctInput)) return dic[correctInput];
}

Then, finally, I still wonder why you would check for upper or lower. What information does it give if it returns true? You still don't know which exactly exists - and that's why you should choose to either map upper or lowercase and stick to it.

Dion V.
  • 2,090
  • 2
  • 14
  • 24
0

just define the Dictionary like that :

Dictionary<string, int> Dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
AlameerAshraf
  • 872
  • 1
  • 13
  • 23