c# - Unexpected results with HashSet.Contains and custom IEqualityComparer -
i must have sort of misunderstanding respect using custom comparer hashset
. collect lot of different types of data store intermediately json. in order operate on using json.net, jobject
, jarray
, , jtoken
.
generally add bit of metadata inline stuff @ collection time, , prefixed "tbp_". need know if particular bit of data, represented jobject
, has been collected before (or not). in order have custom iequalitycomparer
extends implementation provided json.net. strips out metadata before checking value equality using provided implementation:
public class entrycomparer : jtokenequalitycomparer { private static string _excludedprefix = "tbp_"; public jobject cloneforcomparison(jobject obj) { var clone = obj.deepclone() jobject; var propertiestoremove = clone .properties() .where(p => p.name.startswith(_excludedprefix)); foreach (var property in propertiestoremove) { property.remove(); } return clone; } public bool equals(jobject obj1, jobject obj2) { return base.equals(cloneforcomparison(obj1), cloneforcomparison(obj2)); } public int gethashcode(jobject obj) { return base.gethashcode(cloneforcomparison(obj)); } }
i use hashset track data i'm operating on, since need know if exists or not. initialize hashset instance of entrycomparer
. tests are:
public class entrycomparertests { entrycomparer comparer; jobject j1; jobject j2; public entrycomparertests() { comparer = new entrycomparer(); j1 = jobject.parse(@" { 'tbp_entry_date': '2017-03-25t21:25:53.127993-04:00', 'from_date': '1/6/2017', 'to_date': '2/7/2017', 'use': '324320', 'reading': 'act', 'kvars': '0.00', 'demand': '699.10', 'bill_amt': '$28,750.75' }"); j2 = jobject.parse(@" { 'tbp_entry_date': '2017-03-10t18:59:00.537745-05:00', 'from_date': '1/6/2017', 'to_date': '2/7/2017', 'use': '324320', 'reading': 'act', 'kvars': '0.00', 'demand': '699.10', 'bill_amt': '$28,750.75' }"); } [fact] public void test_equality_comparer_gethashcode() { assert.equal(comparer.gethashcode(j1), comparer.gethashcode(j2)); assert.equal(true, comparer.equals(j1, j2)); } [fact] public void test_equality_comparer_hashset_contains() { var hs = new hashset<jobject>(comparer); hs.add(j1); assert.equal(true, hs.contains(j2)); } }
test_equality_comparer_gethashcode()
passes, test_equality_comparer_hashset_contains()
fails. j1
, j2
should treated equal , according results of first test, missing here?
change signature of class:
public class entrycomparer : jtokenequalitycomparer, iequalitycomparer<jobject>
otherwise gethashcode()
, equals()
used ones in base class (that has different "signature"... base class implements iequalitycomparer<jtoken>
, reason methods aren't called hashset<>
).
then there small bug properties removal:
var propertiestoremove = clone .properties() .where(p => p.name.startswith(_excludedprefix)) .toarray();
better "hide" jtokenequalitycomparer
, make private field, like:
public class entrycomparer : iequalitycomparer<jobject> { private static readonly jtokenequalitycomparer _comparer = new jtokenequalitycomparer(); private static readonly string _excludedprefix = "tbp_"; public static jobject cloneforcomparison(jobject obj) { var clone = obj.deepclone() jobject; var propertiestoremove = clone .properties() .where(p => p.name.startswith(_excludedprefix)) .toarray(); foreach (var property in propertiestoremove) { property.remove(); } return clone; } public bool equals(jobject obj1, jobject obj2) { return _comparer.equals(cloneforcomparison(obj1), cloneforcomparison(obj2)); } public int gethashcode(jobject obj) { return _comparer.gethashcode(cloneforcomparison(obj)); } }
Comments
Post a Comment