views:

3806

answers:

6

Hi, I've got a Method that gets a IDictionary as a parameter. Now I want to provide a method that retrieves the value from this dictionary, but it should be case-invariant.

So my solution to this right now was to have a static function that loops through the keys and converts them toLower() like this:

private static IDictionary<ILanguage, IDictionary<string, string>> ConvertKeysToLowerCase(
    IDictionary<ILanguage, IDictionary<string, string>> dictionaries)
{
    IDictionary<ILanguage, IDictionary<string, string>> resultingConvertedDictionaries 
        = new Dictionary<ILanguage, IDictionary<string, string>>();
    foreach(ILanguage keyLanguage in dictionaries.Keys)
    {
        IDictionary<string, string> convertedDictionatry = new Dictionary<string, string>();
        foreach(string key in dictionaries[keyLanguage].Keys)
        {
            convertedDictionatry.Add(key.ToLower(), dictionaries[keyLanguage][key]);
        }
        resultingConvertedDictionaries.Add(keyLanguage, convertedDictionatry);
    }
    return resultingConvertedDictionaries;
}

Now, this is ok, but still it's a pretty huge chunk of code that contradicts my idea of "clean and efficient". Do you know any alternatives to this so that the .ContainsKey() method of the dictionary doesn't differentiate between casing?

+18  A: 

Yes - pass the Dictionary constructor StringComparer.OrdinalIgnoreCase (or another case-ignoring comparer, depending on your culture-sensitivity needs).

Jon

Jon Skeet
Thanks! It worked, but I still need to look through the ILanguage Dictionary and add the <string, string> dictionaries manually. No way I can get rid of this?
Tigraine
Not that I can immediately think of. It's only one loop though... hardly unreadable.If you're using .NET 3.5 you could probably streamline it a *little* bit, but I wouldn't sweat it. Can you change the creator of the original dictionary to specify a case-insensitive comparer in the first place?
Jon Skeet
That seems to be the best solution since Tigraine wants to achieve is case-ignoring comparison and converting the keys to lowercase is just one (unnecessary) way to achieve this.
VVS
I can't change the creator of the IDictionary that gets passed as a parameter. But changing the comparator works perfectly :) Thanks!
Tigraine
Somehow this doesn't feel right. By trying to change the parameter I start depending onto Dictionary instead of only working with IDictionary. I guess I'll have to rework the code that creates the dictionary, or abstract it at least from my retrieval logic.
Tigraine
Why would you need to change the type of the parameter? I don't think you need to depend on being passed a Dictionary.
Jon Skeet
A: 

You could use the var keyword to remove some clutter. Technically the source remains the same. Also I would just pass and return a Dictionary<string, string> because you're not doing anything with that ILanguage parameter and make the method more reusable:

private static IDictionary<string, string> ConvertKeysToLowerCase(
    IDictionary<string, string> dictionaries)
{
    var convertedDictionatry = new Dictionary<string, string>();
    foreach(string key in dictionaries.Keys)
    {
        convertedDictionatry.Add(key.ToLower(), dictionaries[key]);
    }
    return convertedDictionatry;
}

... and call it like so:

// myLanguageDictionaries is of type IDictionary<ILanguage, IDictionary<string, string>>
foreach (var dictionary in myLanguageDictionaries.Keys)
{
  myLanguageDictionaries[dictionary].Value = 
      ConvertKeysToLowerCase(myLanguageDictionaries[dictionary].Value);
}
VVS
Good point, but still doesn't solve the problem with the looping. I'll combine your solution with Jon Skeet's :)Thanks!
Tigraine
Well.. to alter every element in a list you have to touch every element which always results in a loop. Of course you can try to obfuscate that in some way ;)
VVS
+1  A: 

You could inherit from IDictionary yourself, and simply marshal calls to an internal Dictionary instance.

Add(string key, string value) { dictionary.Add(key.ToLowerInvariant(), value) ; }
public string this[string key]
{
    get { return dictionary[key.ToLowerInvariant()]; }
    set { dictionary[key.ToLowerInvariant()] = value; }
}
// And so forth.
Jonathan C Dickinson
A: 

LINQ version using the IEnumerable<T> extension methods:


        private static IDictionary<ILanguage, IDictionary<string, string>> ConvertKeysToLowerCase(
            IDictionary<ILanguage, IDictionary<string, string>> dictionaries)
        {
            return dictionaries.ToDictionary(
                x => x.Key, v => CloneWithComparer(v.Value, StringComparer.OrdinalIgnoreCase));
        }

        static IDictionary<K, V> CloneWithComparer<K,V>(IDictionary<K, V> original, IEqualityComparer<K> comparer)
        {
            return original.ToDictionary(x => x.Key, x => x.Value, comparer);
        }
mancaus
+1  A: 

System.Collections.Specialized.StringDictionary() may help. MSDN states:

"The key is handled in a case-insensitive manner; it is translated to lowercase before it is used with the string dictionary.

In .NET Framework version 1.0, this class uses culture-sensitive string comparisons. However, in .NET Framework version 1.1 and later, this class uses CultureInfo.InvariantCulture when comparing strings. For more information about how culture affects comparisons and sorting, see Comparing and Sorting Data for a Specific Culture and Performing Culture-Insensitive String Operations."

GregUzelac
+1  A: 

By using a StringDictionary the keys are converted to lower case at creating time.

http://simiansoftware.blogspot.com/2008/11/have-const-string-with-ui-description.html