views:

152

answers:

5

Assuming dictionary keys and values have their equals and hash methods implemented correctly, what is the most succinct and efficient way to test for equality of two dictionaries?

In this context two dictionaries are said to be equal if they contain the same set of keys (order not important), and for every such key, they agree on the value.

here are some ways i came up with (there are probably many more):

public bool Compare1<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey,TValue> dic2)
{
    return dic1.OrderBy(x => x.Key).
        SequenceEqual(dic2.OrderBy(x => x.Key));
}

public bool Compare2<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey, TValue> dic2)
{
    return (dic1.Count == dic2.Count && 
        dic1.Intersect(dic2).Count().
        Equals(dic1.Count));
}

public bool Compare3<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey, TValue> dic2)
{
    return (dic1.Intersect(dic2).Count().
        Equals(dic1.Union(dic2).Count()));
}
+4  A: 

It really depends on what you mean by equality.

This method will test that two dictionaries contain the same keys with the same values (assuming that both dictionaries use the same IEqualityComparer<TKey> implementation).

public bool CompareX<TKey, TValue>(
    Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2)
{
    if (dict1 == dict2) return true;
    if ((dict1 == null) || (dict2 == null)) return false;
    if (dict1.Count != dict2.Count) return false;

    var comparer = EqualityComparer<TValue>.Default;

    foreach (KeyValuePair<TKey, TValue> kvp in dict1)
    {
        TValue value2;
        if (!dict2.TryGetValue(kvp.Key, out value2)) return false;
        if (!comparer.Equals(kvp.Value, value2)) return false;
    }
    return true;
}
LukeH
+1 Nice, I like it
w69rdy
aren't you emptying the dictionary? comparex would fail the second time called since the second parameter is empty. why modify a dictionary - doesn't that violate a principle about a simple equality check?
SB
@SB: Very good point. The `CompareX` method shouldn't really mutate either of the dictionaries passed to it. It was supposed to be a simple proof-of-concept, but I'll edit to make it safer.
LukeH
@LukeH: awesome - just wanted to make sure I was reading it properly.
SB
@LukeH: This is a good answer, although most answers here appear to be `O(n log n)` with some fast-rejection going on. I would add a hash-check, which will make it will fast-reject in linear time with *high* probability. E.g. `int hash = dict.Aggregate(0, (hash, kvp) => hash ^ kvp.Key.GetHashCode() ^ kvp.Value.GetHashCode());` It adds nothing to the time-complexity.
Ani
@Ani: I don't really see how that would help. Generating and comparing the hashes will require a pass through both dictionaries, reading keys and values. If we generate and compare a hash of those keys and values then we get a "high probability" result; if we just compare them directly we get an exact answer. Am I overlooking something?
LukeH
When the dictionaries have equal counts, Case 1: They are unequal - the hash will almost certainly reject their equality in linear time. Case 2: They are equal - Your loop enters its worst case where it has to enumerate dict1 fully (`n`) and spend `nlogn` in getting the equivalent kvps out form dict2. In this case, the fast linear-time hash check hardly makes the run-time worse.
Ani
@Ani: Could you give some more details please? How would generating and comparing hashes (generated from keys/values) be any faster than just comparing the keys/values themselves? Pulling the info from `dict2` should be roughly O(1), assuming that the hash codes are half-decent.
LukeH
@LukeH: Ok, fair enough. I suppose it depends on whether you consider the retrieval to be `O(1)` (best case) or `O(log n)` (average case for most hashtables). IMO, the behaviour of most hashes coupled with the growth of the number of hash-buckets typically makes it `O(logn).`
Ani
doesn't the .Net equals semantic require that two null dictionaries be considered equal?
rony l
@rony: The first line of the method takes care of that.
LukeH
@LukeH: yes it does. thanks.
rony l
is this more efficient than Nick's answer? dic1.Count == dic2.Count
rony l
@rony: The `Except` method works in a similar way to my answer. The performance should be very close, although I would expect mine to maybe have a *slight* edge: the `Except` method requires an initial pass through `dic2` to construct a separate set. You'd need to benchmark yourself to be sure, but I'd be surprised if there's any major difference.
LukeH
@LukeH: great, thanks!
rony l
+5  A: 
dic1.Count == dic2.Count && !dic1.Except(dic2).Any();
Nick Jones
This doesn't seem correct to me.
Dan Tao
Sorry, have now added the missing not to make it correct. The dictionaries are equivalent if they are the same size and there are not any elements which are in the first and not in the second.
Nick Jones
@Nick: That seems better ;)
Dan Tao
+1  A: 

You could use linq for the key/value comparisons:

public bool Compare<TKey, TValue>(Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue dict2)
{
    IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default;

    return  dict1.Count == dict2.Count &&
            dict1.Keys.All(key => dict2.ContainsKey(key) && valueComparer.Equals(dict1[key], dict2[key]));
}
Lee
+1  A: 

@Allen's answer:

bool equals = a.Intersect(b).Count() == a.Union(b).Count()

is about arrays but as far as IEnumerable<T> methods are used, it can be used for Dictionary<K,V> too.

abatishchev
A: 

If two dictionaries contain the same keys, but in different order, should they be considered equal? If not, then the dictionaries should be compared by running enumerators through both simultaneously. This will probably be faster than enumerating through one dictionary and looking up each element in the other. If you have advance knowledge that equal dictionaries will have their elements in the same order, such a double-enumeration is probably the way to go.

supercat