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?
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?
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:
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());
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));
}
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));
}
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
;
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.
just define the Dictionary like that :
Dictionary<string, int> Dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);