-
-
Save nicbell/6081098 to your computer and use it in GitHub Desktop.
//Primitive Type Comparison | |
var a = 1; | |
var b = 1; | |
var c = a; | |
console.log(a == b); //true | |
console.log(a === b); //true | |
console.log(a == c); //true | |
console.log(a === c); //true |
//Object comparison | |
var a = { blah: 1 }; | |
var b = { blah: 1 }; | |
var c = a; | |
console.log(a == b); //false | |
console.log(a === b); //false | |
console.log(a == c); //true | |
console.log(a === c); //true |
//How To Compare Object Values | |
var a = { blah: 1 }; | |
var b = { blah: 1 }; | |
var c = a; | |
var d = { blah: 2 }; | |
Object.compare = function (obj1, obj2) { | |
//Loop through properties in object 1 | |
for (var p in obj1) { | |
//Check property exists on both objects | |
if (obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) return false; | |
switch (typeof (obj1[p])) { | |
//Deep compare objects | |
case 'object': | |
if (!Object.compare(obj1[p], obj2[p])) return false; | |
break; | |
//Compare function code | |
case 'function': | |
if (typeof (obj2[p]) == 'undefined' || (p != 'compare' && obj1[p].toString() != obj2[p].toString())) return false; | |
break; | |
//Compare values | |
default: | |
if (obj1[p] != obj2[p]) return false; | |
} | |
} | |
//Check object 2 for any extra properties | |
for (var p in obj2) { | |
if (typeof (obj1[p]) == 'undefined') return false; | |
} | |
return true; | |
}; | |
console.log(Object.compare(a, b)); //true | |
console.log(Object.compare(a, c)); //true | |
console.log(Object.compare(a, d)); //false |
Always glad to see this old thing created in 2013 has got people talking and learning from each other.
Best implementation that I have seen so far is in rambda's "equals" function.
The only thing that I think can be improved (although, I am not sure that this is the right direction) is when both arguments have a "function" type, we could convert the source code to strings and compare them...
Here is the code (I simply put everything into 1 file) from their source:
const _isArray = Array.isArray;
function type(input) {
const typeOf = typeof input;
if (input === null) {
return "Null";
} else if (input === undefined) {
return "Undefined";
} else if (typeOf === "boolean") {
return "Boolean";
} else if (typeOf === "number") {
return Number.isNaN(input) ? "NaN" : "Number";
} else if (typeOf === "string") {
return "String";
} else if (_isArray(input)) {
return "Array";
} else if (typeOf === "symbol") {
return "Symbol";
} else if (input instanceof RegExp) {
return "RegExp";
}
const asStr = input && input.toString ? input.toString() : "";
if (["true", "false"].includes(asStr)) return "Boolean";
if (!Number.isNaN(Number(asStr))) return "Number";
if (asStr.startsWith("async")) return "Async";
if (asStr === "[object Promise]") return "Promise";
if (typeOf === "function") return "Function";
if (input instanceof String) return "String";
return "Object";
}
function parseError(maybeError) {
const typeofError = maybeError.__proto__.toString();
if (!["Error", "TypeError"].includes(typeofError)) return [];
return [typeofError, maybeError.message];
}
function parseDate(maybeDate) {
if (!maybeDate.toDateString) return [false];
return [true, maybeDate.getTime()];
}
function parseRegex(maybeRegex) {
if (maybeRegex.constructor !== RegExp) return [false];
return [true, maybeRegex.toString()];
}
// main function is here
function equals(a, b) {
if (arguments.length === 1) return (_b) => equals(a, _b);
const aType = type(a);
if (aType !== type(b)) return false;
if (aType === "Function") {
return a.name === undefined ? false : a.name === b.name;
}
if (["NaN", "Undefined", "Null"].includes(aType)) return true;
if (aType === "Number") {
if (Object.is(-0, a) !== Object.is(-0, b)) return false;
return a.toString() === b.toString();
}
if (["String", "Boolean"].includes(aType)) {
return a.toString() === b.toString();
}
if (aType === "Array") {
const aClone = Array.from(a);
const bClone = Array.from(b);
if (aClone.toString() !== bClone.toString()) {
return false;
}
let loopArrayFlag = true;
aClone.forEach((aCloneInstance, aCloneIndex) => {
if (loopArrayFlag) {
if (
aCloneInstance !== bClone[aCloneIndex] &&
!equals(aCloneInstance, bClone[aCloneIndex])
) {
loopArrayFlag = false;
}
}
});
return loopArrayFlag;
}
const aRegex = parseRegex(a);
const bRegex = parseRegex(b);
if (aRegex[0]) {
return bRegex[0] ? aRegex[1] === bRegex[1] : false;
} else if (bRegex[0]) return false;
const aDate = parseDate(a);
const bDate = parseDate(b);
if (aDate[0]) {
return bDate[0] ? aDate[1] === bDate[1] : false;
} else if (bDate[0]) return false;
const aError = parseError(a);
const bError = parseError(b);
if (aError[0]) {
return bError[0]
? aError[0] === bError[0] && aError[1] === bError[1]
: false;
}
if (aType === "Object") {
const aKeys = Object.keys(a);
if (aKeys.length !== Object.keys(b).length) {
return false;
}
let loopObjectFlag = true;
aKeys.forEach((aKeyInstance) => {
if (loopObjectFlag) {
const aValue = a[aKeyInstance];
const bValue = b[aKeyInstance];
if (aValue !== bValue && !equals(aValue, bValue)) {
loopObjectFlag = false;
}
}
});
return loopObjectFlag;
}
return false;
}
module.exports = equals;
+1 like on the solution of @DLiblik posted above, which also covers dates and functions. Maybe post it in a separate github repository so it can be found more easily?
@edwinro - done - find the gist here: areEquivalent.js
Covers most cases (including the order of the key property if present)
function isEqual(obj1, obj2) {
function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}
let type = getType(obj1);
// If the two items are not the same type, return false
if (type !== getType(obj2)) return false;
if (type === "array") return areArraysEqual();
if (type === "object") return areObjectsEqual();
if (type === "function") return areFunctionsEqual();
function areArraysEqual() {
// Check length
if (obj1.length !== obj2.length) return false;
// Check each item in the array
for (let i = 0; i < obj1.length; i++) {
if (!isEqual(obj1[i], obj2[i])) return false;
}
// If no errors, return true
return true;
}
function areObjectsEqual() {
if (Object.keys(obj1).length !== Object.keys(obj2).length) return false;
// Check each item in the object
for (let key in obj1) {
if (Object.prototype.hasOwnProperty.call(obj1, key)) {
if (!isEqual(obj1[key], obj2[key])) return false;
}
}
// If no errors, return true
return true;
}
function areFunctionsEqual() {
return obj1.toString() === obj2.toString();
}
function arePrimativesEqual() {
return obj1 === obj2;
}
return arePrimativesEqual();
}
+1 like on the solution of @DLiblik posted above, which also covers dates and functions. Maybe post it in a separate github repository so it can be found more easily?