Generic Dictionary Equality Comparer

Clash Royale CLAN TAG#URR8PPP
up vote
4
down vote
favorite
I created the below comparer to allow me to use a generic dictionary as a key to another generic dictionary.
My GetHashCode implementation creates a hash based on all keys and their values; but I suspect its distribution could be improved?
Also my Equals method returns true only if the two arguments have exactly the same keys, with each key having exactly the same values. However, potentially there's a more efficient approach to this comparison which I've neglected?
using System.Collections.Generic;
using System.Linq;
namespace MyCompany.Collections.Generic
public class DictionaryEqualityComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>>
readonly IEqualityComparer<TKey> keyComparer;
readonly IEqualityComparer<TValue> valueComparer;
public DictionaryEqualityComparer(): this(null, null)
public DictionaryEqualityComparer(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
this.keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
b == null) return (a == null && b == null); //if either value is null return false, or true if both are null
return a.Count == b.Count //unless they have the same number of items, the dictionaries do not match
&& a.Keys.Intersect(b.Keys, keyComparer).Count() == a.Count //unless they have the same keys, the dictionaries do not match
&& a.Keys.Where(key => ValueEquals(a[key], b[key])).Count() == a.Count; //unless each keys' value is the same in both, the dictionaries do not match
public int GetHashCode(IDictionary<TKey, TValue> obj)
//I suspect there's a more efficient formula for even distribution, but this does the job for now
long hashCode = obj.Count;
foreach (var key in obj.Keys)
hashCode += (key?.GetHashCode() ?? 1000) + (obj[key]?.GetHashCode() ?? 0);
hashCode %= int.MaxValue; //ensure we don't go outside the bounds of MinValue-MaxValue
return (int)hashCode; //safe conversion thanks to the above %
private bool ValueEquals(TValue x, TValue y)
return valueComparer.Equals(x, y);
c# .net generics dictionary
add a comment |Â
up vote
4
down vote
favorite
I created the below comparer to allow me to use a generic dictionary as a key to another generic dictionary.
My GetHashCode implementation creates a hash based on all keys and their values; but I suspect its distribution could be improved?
Also my Equals method returns true only if the two arguments have exactly the same keys, with each key having exactly the same values. However, potentially there's a more efficient approach to this comparison which I've neglected?
using System.Collections.Generic;
using System.Linq;
namespace MyCompany.Collections.Generic
public class DictionaryEqualityComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>>
readonly IEqualityComparer<TKey> keyComparer;
readonly IEqualityComparer<TValue> valueComparer;
public DictionaryEqualityComparer(): this(null, null)
public DictionaryEqualityComparer(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
this.keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
b == null) return (a == null && b == null); //if either value is null return false, or true if both are null
return a.Count == b.Count //unless they have the same number of items, the dictionaries do not match
&& a.Keys.Intersect(b.Keys, keyComparer).Count() == a.Count //unless they have the same keys, the dictionaries do not match
&& a.Keys.Where(key => ValueEquals(a[key], b[key])).Count() == a.Count; //unless each keys' value is the same in both, the dictionaries do not match
public int GetHashCode(IDictionary<TKey, TValue> obj)
//I suspect there's a more efficient formula for even distribution, but this does the job for now
long hashCode = obj.Count;
foreach (var key in obj.Keys)
hashCode += (key?.GetHashCode() ?? 1000) + (obj[key]?.GetHashCode() ?? 0);
hashCode %= int.MaxValue; //ensure we don't go outside the bounds of MinValue-MaxValue
return (int)hashCode; //safe conversion thanks to the above %
private bool ValueEquals(TValue x, TValue y)
return valueComparer.Equals(x, y);
c# .net generics dictionary
NB: After posting realised that myGetHashCodemethod doesn't handle nulls; so have since amended the first line toif (obj == null) return 0; long hashCode = obj.Count + 1;; so that null is handled, and an empty dictionary has a different hashcode to a null one.
â JohnLBevan
2 hours ago
add a comment |Â
up vote
4
down vote
favorite
up vote
4
down vote
favorite
I created the below comparer to allow me to use a generic dictionary as a key to another generic dictionary.
My GetHashCode implementation creates a hash based on all keys and their values; but I suspect its distribution could be improved?
Also my Equals method returns true only if the two arguments have exactly the same keys, with each key having exactly the same values. However, potentially there's a more efficient approach to this comparison which I've neglected?
using System.Collections.Generic;
using System.Linq;
namespace MyCompany.Collections.Generic
public class DictionaryEqualityComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>>
readonly IEqualityComparer<TKey> keyComparer;
readonly IEqualityComparer<TValue> valueComparer;
public DictionaryEqualityComparer(): this(null, null)
public DictionaryEqualityComparer(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
this.keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
b == null) return (a == null && b == null); //if either value is null return false, or true if both are null
return a.Count == b.Count //unless they have the same number of items, the dictionaries do not match
&& a.Keys.Intersect(b.Keys, keyComparer).Count() == a.Count //unless they have the same keys, the dictionaries do not match
&& a.Keys.Where(key => ValueEquals(a[key], b[key])).Count() == a.Count; //unless each keys' value is the same in both, the dictionaries do not match
public int GetHashCode(IDictionary<TKey, TValue> obj)
//I suspect there's a more efficient formula for even distribution, but this does the job for now
long hashCode = obj.Count;
foreach (var key in obj.Keys)
hashCode += (key?.GetHashCode() ?? 1000) + (obj[key]?.GetHashCode() ?? 0);
hashCode %= int.MaxValue; //ensure we don't go outside the bounds of MinValue-MaxValue
return (int)hashCode; //safe conversion thanks to the above %
private bool ValueEquals(TValue x, TValue y)
return valueComparer.Equals(x, y);
c# .net generics dictionary
I created the below comparer to allow me to use a generic dictionary as a key to another generic dictionary.
My GetHashCode implementation creates a hash based on all keys and their values; but I suspect its distribution could be improved?
Also my Equals method returns true only if the two arguments have exactly the same keys, with each key having exactly the same values. However, potentially there's a more efficient approach to this comparison which I've neglected?
using System.Collections.Generic;
using System.Linq;
namespace MyCompany.Collections.Generic
public class DictionaryEqualityComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>>
readonly IEqualityComparer<TKey> keyComparer;
readonly IEqualityComparer<TValue> valueComparer;
public DictionaryEqualityComparer(): this(null, null)
public DictionaryEqualityComparer(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
this.keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
b == null) return (a == null && b == null); //if either value is null return false, or true if both are null
return a.Count == b.Count //unless they have the same number of items, the dictionaries do not match
&& a.Keys.Intersect(b.Keys, keyComparer).Count() == a.Count //unless they have the same keys, the dictionaries do not match
&& a.Keys.Where(key => ValueEquals(a[key], b[key])).Count() == a.Count; //unless each keys' value is the same in both, the dictionaries do not match
public int GetHashCode(IDictionary<TKey, TValue> obj)
//I suspect there's a more efficient formula for even distribution, but this does the job for now
long hashCode = obj.Count;
foreach (var key in obj.Keys)
hashCode += (key?.GetHashCode() ?? 1000) + (obj[key]?.GetHashCode() ?? 0);
hashCode %= int.MaxValue; //ensure we don't go outside the bounds of MinValue-MaxValue
return (int)hashCode; //safe conversion thanks to the above %
private bool ValueEquals(TValue x, TValue y)
return valueComparer.Equals(x, y);
c# .net generics dictionary
c# .net generics dictionary
edited 3 hours ago
t3chb0t
32.8k744105
32.8k744105
asked 3 hours ago
JohnLBevan
6451520
6451520
NB: After posting realised that myGetHashCodemethod doesn't handle nulls; so have since amended the first line toif (obj == null) return 0; long hashCode = obj.Count + 1;; so that null is handled, and an empty dictionary has a different hashcode to a null one.
â JohnLBevan
2 hours ago
add a comment |Â
NB: After posting realised that myGetHashCodemethod doesn't handle nulls; so have since amended the first line toif (obj == null) return 0; long hashCode = obj.Count + 1;; so that null is handled, and an empty dictionary has a different hashcode to a null one.
â JohnLBevan
2 hours ago
NB: After posting realised that my
GetHashCode method doesn't handle nulls; so have since amended the first line to if (obj == null) return 0; long hashCode = obj.Count + 1;; so that null is handled, and an empty dictionary has a different hashcode to a null one.â JohnLBevan
2 hours ago
NB: After posting realised that my
GetHashCode method doesn't handle nulls; so have since amended the first line to if (obj == null) return 0; long hashCode = obj.Count + 1;; so that null is handled, and an empty dictionary has a different hashcode to a null one.â JohnLBevan
2 hours ago
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
3
down vote
It looks pretty OK to me, may be I would create Equals(...) like:
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
if (a == null
It should be sufficient to look up by one of the dictionaries, because we know they are equal in size, so an entry in a should find an equal entry in b if the are equal all in all. I haven't tested it but it seems that it should be faster compared to Intersect().Count() && ...Where() because there is only one iteration/lookup?
2
Nice one; that's a great solution. I may swap!a.Any(kvpA => !b.TryGetValue(kvpA.Key, out TValue vB) || !valueComparer.Equals(kvpA.Value, vB));fora.All(kvpA => b.TryGetValue(kvpA.Key, out TValue vB) && valueComparer.Equals(kvpA.Value, vB));to remove a few double negatives, but really like this approach; thanks for the tip.
â JohnLBevan
30 mins ago
@JohnLBevan: that's better and equally efficient.
â Henrik Hansen
6 mins ago
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
It looks pretty OK to me, may be I would create Equals(...) like:
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
if (a == null
It should be sufficient to look up by one of the dictionaries, because we know they are equal in size, so an entry in a should find an equal entry in b if the are equal all in all. I haven't tested it but it seems that it should be faster compared to Intersect().Count() && ...Where() because there is only one iteration/lookup?
2
Nice one; that's a great solution. I may swap!a.Any(kvpA => !b.TryGetValue(kvpA.Key, out TValue vB) || !valueComparer.Equals(kvpA.Value, vB));fora.All(kvpA => b.TryGetValue(kvpA.Key, out TValue vB) && valueComparer.Equals(kvpA.Value, vB));to remove a few double negatives, but really like this approach; thanks for the tip.
â JohnLBevan
30 mins ago
@JohnLBevan: that's better and equally efficient.
â Henrik Hansen
6 mins ago
add a comment |Â
up vote
3
down vote
It looks pretty OK to me, may be I would create Equals(...) like:
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
if (a == null
It should be sufficient to look up by one of the dictionaries, because we know they are equal in size, so an entry in a should find an equal entry in b if the are equal all in all. I haven't tested it but it seems that it should be faster compared to Intersect().Count() && ...Where() because there is only one iteration/lookup?
2
Nice one; that's a great solution. I may swap!a.Any(kvpA => !b.TryGetValue(kvpA.Key, out TValue vB) || !valueComparer.Equals(kvpA.Value, vB));fora.All(kvpA => b.TryGetValue(kvpA.Key, out TValue vB) && valueComparer.Equals(kvpA.Value, vB));to remove a few double negatives, but really like this approach; thanks for the tip.
â JohnLBevan
30 mins ago
@JohnLBevan: that's better and equally efficient.
â Henrik Hansen
6 mins ago
add a comment |Â
up vote
3
down vote
up vote
3
down vote
It looks pretty OK to me, may be I would create Equals(...) like:
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
if (a == null
It should be sufficient to look up by one of the dictionaries, because we know they are equal in size, so an entry in a should find an equal entry in b if the are equal all in all. I haven't tested it but it seems that it should be faster compared to Intersect().Count() && ...Where() because there is only one iteration/lookup?
It looks pretty OK to me, may be I would create Equals(...) like:
public bool Equals(IDictionary<TKey, TValue> a, IDictionary<TKey, TValue> b)
if (a == null
It should be sufficient to look up by one of the dictionaries, because we know they are equal in size, so an entry in a should find an equal entry in b if the are equal all in all. I haven't tested it but it seems that it should be faster compared to Intersect().Count() && ...Where() because there is only one iteration/lookup?
answered 42 mins ago
Henrik Hansen
5,4081620
5,4081620
2
Nice one; that's a great solution. I may swap!a.Any(kvpA => !b.TryGetValue(kvpA.Key, out TValue vB) || !valueComparer.Equals(kvpA.Value, vB));fora.All(kvpA => b.TryGetValue(kvpA.Key, out TValue vB) && valueComparer.Equals(kvpA.Value, vB));to remove a few double negatives, but really like this approach; thanks for the tip.
â JohnLBevan
30 mins ago
@JohnLBevan: that's better and equally efficient.
â Henrik Hansen
6 mins ago
add a comment |Â
2
Nice one; that's a great solution. I may swap!a.Any(kvpA => !b.TryGetValue(kvpA.Key, out TValue vB) || !valueComparer.Equals(kvpA.Value, vB));fora.All(kvpA => b.TryGetValue(kvpA.Key, out TValue vB) && valueComparer.Equals(kvpA.Value, vB));to remove a few double negatives, but really like this approach; thanks for the tip.
â JohnLBevan
30 mins ago
@JohnLBevan: that's better and equally efficient.
â Henrik Hansen
6 mins ago
2
2
Nice one; that's a great solution. I may swap
!a.Any(kvpA => !b.TryGetValue(kvpA.Key, out TValue vB) || !valueComparer.Equals(kvpA.Value, vB)); for a.All(kvpA => b.TryGetValue(kvpA.Key, out TValue vB) && valueComparer.Equals(kvpA.Value, vB)); to remove a few double negatives, but really like this approach; thanks for the tip.â JohnLBevan
30 mins ago
Nice one; that's a great solution. I may swap
!a.Any(kvpA => !b.TryGetValue(kvpA.Key, out TValue vB) || !valueComparer.Equals(kvpA.Value, vB)); for a.All(kvpA => b.TryGetValue(kvpA.Key, out TValue vB) && valueComparer.Equals(kvpA.Value, vB)); to remove a few double negatives, but really like this approach; thanks for the tip.â JohnLBevan
30 mins ago
@JohnLBevan: that's better and equally efficient.
â Henrik Hansen
6 mins ago
@JohnLBevan: that's better and equally efficient.
â Henrik Hansen
6 mins ago
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f206645%2fgeneric-dictionary-equality-comparer%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password

NB: After posting realised that my
GetHashCodemethod doesn't handle nulls; so have since amended the first line toif (obj == null) return 0; long hashCode = obj.Count + 1;; so that null is handled, and an empty dictionary has a different hashcode to a null one.â JohnLBevan
2 hours ago