-
-
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 |
+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?
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();
}
Here's what I've used over the years - easy to modify for special cases if you need to (i.e. if you want to insert a different compare for functions, etc.):