Skip to content

Instantly share code, notes, and snippets.

@dSalieri
Last active September 22, 2021 06:22
Show Gist options
  • Save dSalieri/4b3cdd9dee0743ca799e7eb5f5786bba to your computer and use it in GitHub Desktop.
Save dSalieri/4b3cdd9dee0743ca799e7eb5f5786bba to your computer and use it in GitHub Desktop.
Compares two objects on equality
/// 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