Last active
September 22, 2021 06:22
-
-
Save dSalieri/4b3cdd9dee0743ca799e7eb5f5786bba to your computer and use it in GitHub Desktop.
Compares two objects on equality
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Compares two objects and returns true or false | |
/// No protection for recursive structure of objects, don't compare recursive objects | |
/// Comparation doesn't provide exotic objects like Set, Map or Date, you should realize them by yourself if you want compare it too | |
/// Below there is demonstration how to realize and include exotic objects into main algorithm | |
function objectsEquality(specialTypes) { | |
specialTypes = sortOf(specialTypes) === "object" ? specialTypes : {}; | |
return function equality(o1, o2, options) { | |
/// if arguments that must be objects aren't them, value is returned null | |
if ([o1, o2].some((i) => typeof i !== "object")) return null; | |
/// Objects are equal, if objects have same reference to object | |
if (o1 === o2) return true; | |
/// If types of objects different, then objects aren't equal | |
if (Object.prototype.toString.call(o1) !== Object.prototype.toString.call(o2)) return false; | |
/// If the first object neither an "object" or "array" then one of provided functions in the list is invoked | |
/// Else create descriptors for both objects | |
switch (sortOf(o1)) { | |
case "object": | |
case "array": | |
break; | |
default: { | |
if (Object.hasOwnProperty.call(specialTypes, sortOf(o1))) { | |
return specialTypes[sortOf(o1)](o1, o2, equality); | |
} | |
} | |
} | |
/// Creating descriptors for objects | |
o1 = Object.getOwnPropertyDescriptors(o1); | |
o2 = Object.getOwnPropertyDescriptors(o2); | |
options = options || false; | |
/// Comparison via keys of first object | |
for (let key in o1) { | |
/// Exclude properties from the first object that doesn't have own properties | |
if (!Object.hasOwnProperty.call(o1, key)) continue; | |
/// Validation for existing key in compared object | |
if (!Object.hasOwnProperty.call(o2, key)) return false; | |
/// Pass the properties if they are equal | |
if (o1[key].value === o2[key].value) { | |
/// Validation of corresponding to properties descriptors | |
if ((options === true || options.enumerable === true) && o1[key].enumerable !== o2[key].enumerable) return false; | |
if ((options === true || options.writable === true) && o1[key].writable !== o2[key].writable) return false; | |
if ((options === true || options.configurable === true) && o1[key].configurable !== o2[key].configurable) return false; | |
continue; | |
} | |
/// If value is not an object, then it's primitive value and objects are not equal | |
if (typeof o1[key].value !== "object") { | |
/// Checking NaN values | |
if (o1[key].value !== o2[key].value && Number.isNaN(o1[key].value) && Number.isNaN(o2[key].value)) continue; | |
/// Only primitive values returns | |
return false; | |
} | |
/// If value is object, then core of function is invoked (recursively) | |
if (!equality(o1[key].value, o2[key].value, options)) return false; | |
} | |
/// Comparison via keys of second object, by their absense | |
for (let key in o2) { | |
if (Object.hasOwnProperty.call(o2, key) && !Object.hasOwnProperty.call(o1, key)) return false; | |
} | |
return true; | |
}; | |
function sortOf(arg) { | |
return Object.prototype.toString.call(arg).slice(8, -1).toLowerCase(); | |
} | |
} | |
/// Realization of exotic objects for compare | |
let extraTypes = { | |
set: function (set1, set2, equality) { | |
let it1 = set1[Symbol.iterator](); | |
let it2 = set2[Symbol.iterator](); | |
while (true) { | |
let { value: v1, done: d1 } = it1.next(); | |
let { value: v2, done: d2 } = it2.next(); | |
if (typeof v1 === "object" && typeof v2 === "object") | |
if (equality(v1, v2)) continue; | |
else return false; | |
if (d1 === true || d2 === true) { | |
if (d1 === d2) return true; | |
else return false; | |
} | |
if (v1 !== v2) return false; | |
} | |
}, | |
map: function (map1, map2, equality) { | |
let it1 = map1[Symbol.iterator](); | |
let it2 = map2[Symbol.iterator](); | |
while (true) { | |
let { value: v1, done: d1 } = it1.next(); | |
let { value: v2, done: d2 } = it2.next(); | |
if (typeof v1 === "object" && typeof v2 === "object") | |
if (equality(v1, v2)) continue; | |
else return false; | |
if (d1 === true || d2 === true) { | |
if (d1 === d2) return true; | |
else return false; | |
} | |
if (v1[0] !== v2[0] || v1[1] !== v2[1]) return false; | |
} | |
}, | |
date: function (date1, date2, equality) { | |
return date1.getTime() === date2.getTime(); | |
}, | |
}; | |
/// Creating required objects for comparison | |
let compareExtended = objectsEquality(extraTypes); | |
let currentDate = new Date(); | |
let o1 = { | |
a: 100, | |
b: new Set([1, 2, 3]), | |
c: new Map([ | |
["fire", 100], | |
["air", 200], | |
["water", 300], | |
]), | |
d: currentDate, | |
}; | |
let o2 = { | |
a: 100, | |
b: new Set([1, 2, 3]), | |
c: new Map([ | |
["fire", 100], | |
["air", 200], | |
["water", 300], | |
]), | |
d: currentDate, | |
}; | |
let o3 = { | |
a: 100, | |
b: new Set([1, 2, 3]), | |
c: new Map([ | |
["fire", 100], | |
["air", 200], | |
["water", 400], | |
]), | |
d: currentDate, | |
}; | |
/// Testing | |
compareExtended(o1, o2); /// true | |
compareExtended(o1, o3); /// false |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment