Case insensitive dictionary with tuple key

I have a dictionary where the key is Tuple, where the first element is a date and the second element is a string. I would like the dictionary to be case insensitive.

I know that if the key were just a string, I could pass StringComparer.OrdinalIgnoreCase as a parameter when declaring the dictionary, but this does not seem to work when the key is Tuple.

Is there a way to tell StringComparer to use in the second Tuple element?

thanks

+7
source share
3 answers

Use this Dictionary constructor overload , which allows you to specify a custom key mapper. You would accompany this with the creation of a class that implements

 IEqualityComparer<Tuple<string, DateTime>> 

What might look like this:

 class CustomEqualityComparer : IEqualityComparer<Tuple<string, DateTime>> { public bool Equals(Tuple<string, DateTime> lhs, Tuple<string, DateTime> rhs) { return StringComparer.CurrentCultureIgnoreCase.Equals(lhs.Item1, rhs.Item1) && lhs.Item2 == rhs.Item2; } public int GetHashCode(Tuple<string, DateTime> tuple) { return StringComparer.CurrentCultureIgnoreCase.GetHashCode(tuple.Item1) ^ tuple.Item2.GetHashCode(); } } 

There are no arguments here, so please do not consider this as production code. In addition, care must be taken to ensure that the Equals and GetHashCode implementations satisfy the overarching condition that if two tuples are compared equal, they must have the same hash code. When dealing with custom text comparisons, it is easy to introduce errors if they are not more careful: for example, using ToLowerInvariant instead of ToLower above would be an error (albeit without a surface for some time).

+13
source

Since comparisons will be case insensitive, you can use the toLower / toUpper in the string part when creating tuples, and then always omit or align the rows that you will have in tuples used to extract / compare entries in the dictionary.

+1
source

I need this in the Dictionary<Tuple<>> wrapper, so I used @Jon code to create a generic version

 public class TupleEqualityComparer<T1, T2> : IEqualityComparer<Tuple<T1, T2>> { private IEqualityComparer<T1> comparer1; private IEqualityComparer<T2> comparer2; public TupleEqualityComparer(IEqualityComparer<T1> comparer1, IEqualityComparer<T2> comparer2) { this.comparer1 = comparer1 ?? EqualityComparer<T1>.Default; this.comparer2 = comparer2 ?? EqualityComparer<T2>.Default; } public bool Equals(Tuple<T1, T2> lhs, Tuple<T1, T2> rhs) { return comparer1.Equals(lhs.Item1, rhs.Item1) && comparer2.Equals(lhs.Item2, rhs.Item2); } public int GetHashCode(Tuple<T1, T2> tuple) { return comparer1.GetHashCode(tuple.Item1) ^ comparer2.GetHashCode(tuple.Item2); } } public class Dictionary<TKey1, TKey2, TValue> : Dictionary<Tuple<TKey1, TKey2>, TValue>() { public Dictionary() : base() { } public Dictionary(IEqualityComparer<TKey1> comparer1, IEqualityComparer<TKey2> comparer2) : base(new TupleEqualityComparer<TKey1, Tkey2>(comparer1, comparer2) { } public TValue this[TKey1 key1, TKey2 key2] { get { return base[Tuple.Create(key1, key2)]; } } public void Add(TKey1 key1, TKey2 key2, TValue value) { base.Add(Tuple.Create(key1, key2), value); } public bool ContainsKey(TKey1 key1, TKey2 key2) { return base.ContainsKey(Tuple.Create(key1, key2)); } public bool TryGetValue(TKey1 key1, TKey2 key2, out TValue value) { return base.TryGetValue(Tuple.Create(key1, key2), out value); } } 

Using

 var dict = new Dictionary<string, DateTime, int>( StringComparer.OrdinalIgnoreCase, null); dict.Add("value1", DateTime.Now, 123); Assert.IsTrue(dict.ContainsKey("VALUe1")); 
0
source

All Articles