Comparing two Dictionaries in C#
"What's going on" is it's comparing KeyValuePair
entries for the two dictionaries, in order. Dictionaries are inherently unordered - you shouldn't be relying on anything about the order in which entries come out of them. If you use:
firstDictionary.OrderBy(pair => pair.Key)
.SequenceEqual(secondDictionary.OrderBy(pair => pair.Key))
I suspect you'll find that matches. It's a pretty unpleasant way to compare them though :)
Jon Skeet has already given a good explanation.
However if all you (or anyone else reading this question) want is an efficient method of comparing dictionaries here is a simple Linq-based extension that will do just that:
/// <summary>
/// Compares two dictionaries for equality.
/// </summary>
/// <returns>
/// True if the dictionaries have equal contents or are both null, otherwise false.
/// </returns>
public static bool DictionaryEqual<TKey, TValue>(
this IDictionary<TKey, TValue> dict1, IDictionary<TKey, TValue> dict2,
IEqualityComparer<TValue> equalityComparer = null)
{
if (dict1 == dict2)
return true;
if (dict1 == null | dict2 == null)
return false;
if (dict1.Count != dict2.Count)
return false;
if (equalityComparer == null)
equalityComparer = EqualityComparer<TValue>.Default;
return dict1.All(kvp =>
{
TValue value2;
return dict2.TryGetValue(kvp.Key, out value2)
&& equalityComparer.Equals(kvp.Value, value2);
});
}
It does perhaps look a bit fluffy, but I wanted good readability (and null tests).
So if all you want is a "one-liner" and you already know that both dictionaries are non-null and that the TValue
type overrides the Equals method properly, then you only really need this much (sans the null-checks if TValue
is a valuetype of course):
bool isEqual = dict1.Count == dict2.Count && dict1.All(kvp =>
{
TValue value2;
return dict2.TryGetValue(kvp.Key, out value2)
&& (kvp.Value == null ? value2 == null : kvp.Value.Equals(value2));
});
If you want to do a compare where the dictionaries doesn't have to have the same type of value, or if you prefer to use a delegate or lambda expression instead of having to implement an IEqualityComparer, this extension will do the trick for you instead:
/// <summary>
/// Compares two dictionaries for equality using a custom value equality function.
/// </summary>
/// <returns>
/// True if both dictionaries are null or both have the same set of keys and comparing
/// their respective values for each key using the <paramref name="valueEqualityFunc"/>
/// returns true, otherwise false.
/// </returns>
public static bool DictionaryEqual<TKey, TValue1, TValue2>(
this IDictionary<TKey, TValue1> dict1, IDictionary<TKey, TValue2> dict2,
Func<TValue1, TValue2, bool> valueEqualityFunc)
{
if (valueEqualityFunc == null)
throw new ArgumentNullException("valueEqualityFunc");
if (dict1 == dict2)
return true;
if (dict1 == null | dict2 == null)
return false;
if (dict1.Count != dict2.Count)
return false;
return dict1.All(kvp =>
{
TValue2 value2;
return dict2.TryGetValue(kvp.Key, out value2)
&& valueEqualityFunc(kvp.Value, value2);
});
}
As you can see it's pretty much the same thing as before.
Here is a usage example:
var d1 = new Dictionary<string, string>();
var d2 = new Dictionary<string, string>();
d1.Add("key1", "dog");
d2.Add("key1", "Dog");
d1.Add("key2", "CAT");
d2.Add("key2", "cat");
bool isEqual = DictionaryEqual(d1, d2,
(s1, s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase));
If you run the above code isEqual
will become true.
Caution: As user LukeSchoen points out in the comments, this method might fail to give the expected result if the dictionaries being tested don't use the same EqualityComparer for comparing keys (remember that you can optionally specify an equality comparer for your keys in the dictionary constructor, e.g. to use case-insensitive string keys).
This cannot be fixed since one cannot generalize what's "expected" from trying to equate two dictionaries that use different definitions of equality.
The only solution in the general case is to let the caller specify their own EqaulityComparer for the inter-dictionary key comparison, similarly to how the code above lets the caller specify how values are compared, thus making it the callers responsibility to provide a sensible definition of equality for their use case.