Today I finished reading Ch. 4 in Eloquent JS and I'm struggling to understand how to perform a deep comparison between objects and their properties, particularly through the use of a recursive call. I know my solution below is quite naive and a bit bulky, but I'm trying to wrap my head around all these new things I'm still learning! Just less than a month in on programming :) I would appreciate any tips and help you might have in improving the code and also if you could help me better understand the recursion that needs to occur. Thank you in advance!
Write a function, deepEqual, that takes two values and returns true only if they are the same value or are objects with the same properties whose values are also equal when compared with a recursive call to deepEqual.
var obj = {here: {is: "an"}, object: 2};
var obj1 = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj,obj1));
My Code:
function objectTester(x) {
if (typeof x === 'object' && x !== null)
return true;
}
function deepEqual(valOne, valTwo) {
if (valOne === valTwo) return true;
var comp1 = objectTester(valOne);
var comp2 = objectTester(valTwo);
if (comp1 === comp2) {
var count1;
var count2;
for (var prop in valOne) {
count1++
return count1;
}
for (var prop in valTwo) {
count2++
return count2;
}
if (count1 === count2) {
// This is where I'm getting stuck, not sure how I can recurisvely compare
// two arguments that are to be compared.
}
}
We cannot just implement “==” or “===” operator to compare two objects. The better way to do the comparison is to use JSON. Stringify and compare the objects.
Using the isEqual() method from this library, we can perform a deep comparison between the given operands. It will return a Boolean value indicating whether the operands are equal based on JavaScript strict equality (===) on all attributes of the two given objects.
deepEqual() method tests if two objects, and their child objects, are equal, using the == operator. If the two objects are not equal, an assertion failure is being caused, and the program is terminated. To compare the objects using the === operator, use the assert.
JavaScript provides 3 ways to compare values: The strict equality operator === The loose equality operator == Object.is() function.
Probably easiest to just post a function that does the job with plenty of comments. The recursive part is near the bottom, in the function passed to every:
// Helper to return a value's internal object [[Class]]
// That this returns [object Type] even for primitives
function getClass(obj) {
return Object.prototype.toString.call(obj);
}
/*
** @param a, b - values (Object, RegExp, Date, etc.)
** @returns {boolean} - true if a and b are the object or same primitive value or
** have the same properties with the same values
*/
function objectTester(a, b) {
// If a and b reference the same value, return true
if (a === b) return true;
// If a and b aren't the same type, return false
if (typeof a != typeof b) return false;
// Already know types are the same, so if type is number
// and both NaN, return true
if (typeof a == 'number' && isNaN(a) && isNaN(b)) return true;
// Get internal [[Class]]
var aClass = getClass(a);
var bClass = getClass(b)
// Return false if not same class
if (aClass != bClass) return false;
// If they're Boolean, String or Number objects, check values
if (aClass == '[object Boolean]' || aClass == '[object String]' || aClass == '[object Number]') {
return a.valueOf() == b.valueOf();
}
// If they're RegExps, Dates or Error objects, check stringified values
if (aClass == '[object RegExp]' || aClass == '[object Date]' || aClass == '[object Error]') {
return a.toString() == b.toString();
}
// Otherwise they're Objects, Functions or Arrays or some kind of host object
if (typeof a == 'object' || typeof a == 'function') {
// For functions, check stringigied values are the same
// Almost certainly false if a and b aren't trivial
// and are different functions
if (aClass == '[object Function]' && a.toString() != b.toString()) return false;
var aKeys = Object.keys(a);
var bKeys = Object.keys(b);
// If they don't have the same number of keys, return false
if (aKeys.length != bKeys.length) return false;
// Check they have the same keys
if (!aKeys.every(function(key){return b.hasOwnProperty(key)})) return false;
// Check key values - uses ES5 Object.keys
return aKeys.every(function(key){
return objectTester(a[key], b[key])
});
}
return false;
}
The tests on Date, RegExp, Error, etc. should probably return false if the values/strings aren't the same then fall through to the property checks, but do that only if you think someone might attach properties to Number objects (it's extremely rare to use Number objects, much less add properties to them, but I guess it might happen).
Here it is:
/*
** @param a, b - values (Object, RegExp, Date, etc.)
** @returns {boolean} - true if a and b are the object or same primitive value or
** have the same properties with the same values
*/
function objectTester(a, b) {
// If a and b reference the same value, return true
if (a === b) return true;
// If a and b aren't the same type, return false
if (typeof a != typeof b) return false;
// Already know types are the same, so if type is number
// and both NaN, return true
if (typeof a == 'number' && isNaN(a) && isNaN(b)) return true;
// Get internal [[Class]]
var aClass = getClass(a);
var bClass = getClass(b)
// Return false if not same class
if (aClass != bClass) return false;
// If they're Boolean, String or Number objects, check values
if (aClass == '[object Boolean]' || aClass == '[object String]' || aClass == '[object Number]') {
if (a.valueOf() != b.valueOf()) return false;
}
// If they're RegExps, Dates or Error objects, check stringified values
if (aClass == '[object RegExp]' || aClass == '[object Date]' || aClass == '[object Error]') {
if (a.toString() != b.toString()) return false;
}
// For functions, check stringigied values are the same
// Almost impossible to be equal if a and b aren't trivial
// and are different functions
if (aClass == '[object Function]' && a.toString() != b.toString()) return false;
// For all objects, (including Objects, Functions, Arrays and host objects),
// check the properties
var aKeys = Object.keys(a);
var bKeys = Object.keys(b);
// If they don't have the same number of keys, return false
if (aKeys.length != bKeys.length) return false;
// Check they have the same keys
if (!aKeys.every(function(key){return b.hasOwnProperty(key)})) return false;
// Check key values - uses ES5 Object.keys
return aKeys.every(function(key){
return objectTester(a[key], b[key])
});
return false;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With