1

I have a Dictionary<dynamic, string> and when checking if the dictionary contains a key, if the key is a string I would like to ignore the case. Is this possible?

Brian Campbell
  • 161
  • 1
  • 12
  • 2
    Why `dynamic`? How are you using a dictionary with dynamic as key? This sounds like an [XY problem](https://meta.stackexchange.com/q/66377/386424). What is your end goal? Why are you trying to do this? – Igor Jul 26 '18 at 15:22
  • 2
    Use the Dictionary constructor that lets you pass your own `IEqualityComparer` implementation. Similar to https://stackoverflow.com/a/46143745/17034. Do beware GetHashCode(), it needs to produce a case-insensitive value as well. Lowercase the string when appropriate. – Hans Passant Jul 26 '18 at 15:30
  • @HansPassant, I looked into that solution but was unable to implement IEqualityComparer with dynamic. I don't think that's possible in C#. – Brian Campbell Jul 26 '18 at 18:46
  • @Igor Why dynamic is a legitimate question. Without going into all of the details, I have a method that returns a collection of Dictionaries. I was originally hoping to use generic T as the key instead of dynamic, since the keys in a given dictionary will all be the same type. But the types of the keys will not be the same in all of the dictionaries, so T was not possible. I could use 'object' instead of 'dynamic' I guess, but I believe I still end up with the same problem. – Brian Campbell Jul 26 '18 at 18:53
  • @Igor I can give an example reason for C#/MVC; I have to write a filter that checks for the existence of a property in context.ModelState and if it exists validate it against a property of the auth token. The system is a brownfield and I don't know if anyone has written models with varying cases for this property. – C Bauer Oct 03 '18 at 13:33

2 Answers2

2

You could add an extension to the Dictionary which determines if the key is of type string, and if so, uses case insensitive comparison; otherwise, it uses the default comparison.

public static class DictionaryExtension
{
    public static bool ContainsKeyIgnoreCase<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
    {
        bool? keyExists;

        var keyString = key as string;
        if (keyString != null)
        {
            // Key is a string.
            // Using string.Equals to perform case insensitive comparison of the dictionary key.
            keyExists =
                dictionary.Keys.OfType<string>()
                .Any(k => string.Equals(k, keyString, StringComparison.InvariantCultureIgnoreCase));
        }
        else
        {
            // Key is any other type, use default comparison.
            keyExists = dictionary.ContainsKey(key);
        }

        return keyExists ?? false;
    }
}

You can then use it like this:

var foo = new Foo();
var dictionary =
    new Dictionary<dynamic, string>
    {
        { 1, "One" },     // key is numeric
        { "Two", "Two" }, // key is string
        { foo, "Foo" }    // key is object
    };

dictionary.ContainsKeyIgnoreCase("two");     // Returns true
dictionary.ContainsKeyIgnoreCase("TwO");     // Returns true
dictionary.ContainsKeyIgnoreCase("aBc");     // Returns false
dictionary.ContainsKeyIgnoreCase(1);         // Returns true
dictionary.ContainsKeyIgnoreCase(2);         // Returns false
dictionary.ContainsKeyIgnoreCase(foo);       // Returns true
dictionary.ContainsKeyIgnoreCase(new Foo()); // Returns false

Note:
The extension example above is using StringComparer.InvariantCultureIgnoreCase. You may need to modify the comparison for your needs.

Darren H
  • 450
  • 3
  • 5
1

There's no reasonable way you could implement a case-insensitive get on a case-sensitive hash map.

Although you can create a new case-insensitive dictionary with the contents of an existing case-sensitive dictionary (if you're sure there are no case collisions):-

var oldDictionary = ...;
var comparer = StringComparer.OrdinalIgnoreCase;
var newDictionary = new Dictionary<string, int>(oldDictionary, comparer);

Let me know, if it works.