0

Edit: This is not a duplicate question. My function is different and doesn't use direct object comparison methods.

I am working through Eloquent Javascript book and in the fourth chapter, the last exercise required a function deepEqual that takes two arguments and compares them.

  • If the arguments are different types, it should return false.
  • If the arguments are the same type and same value, it should return true otherwise, return false.
  • If the arguments are both objects, their properties and values should be compared. The function call should return true if both objects have same properties and value in the same order. For example, if we have the following objects: object1 = {a: 1, b: 2}, object2 = {a: 1, b: 2}, object3 = {a: 2, b: 1}, comparing object1 and object2 should return true, but comparing object1 and object3 should return false.
  • Lastly, if the objects have nested objects as parameters, a recursive call of deepEqual to compare the nested objects.

So, here is my solution. It is quite convoluted with if/else statements, pardon it.

function deepEqual(obj1, obj2){

  var checkForSameTypes = (typeof obj1 === typeof obj2);

  if(checkForSameTypes === true){

    //same type but not objects
    if(typeof obj1 !== "object" && typeof obj2 !== "object") {
      if(obj1 == obj2){
        return true;
      }else{
        return false;
      }

     // same type and objects
    }else if(typeof obj1 === "object" && typeof obj2 === "object"){

      //loop first object and compare properties with 2nd object
      for(var prop in obj1){
        if(obj2.hasOwnProperty(prop)) {

        //if 2nd object has property "prop" compare
          if(typeof obj1[prop] === "object" && typeof obj2[prop] === "object"){
            //recursive call
            deepEqual(obj1[prop], obj2[prop]);
          }else if(obj1[prop] === obj2[prop]) {
            //loop goes on to the next property
            continue; 
          }else if(obj1[prop] !== obj2[prop]){
            return false;
          } 
        //it doesn't have property "prop" so return false
        } else {
          return false;
        }
      }
      //finishes loop and all properties are the same
      return true; 
    }
   // they are not the same types so return false
  } else {
    return false;
  }
}

PROBLEM

The function works fine except when an argument is an object with nested objects. Take these objects for example

var type = {go: {inside: "yes"}, come: 2}

var see = {go: {inside: "no"}, come: 2} 

var fine = {when: 3, go: 4};`

When I compare type and see with a call to deepEqual(type,see);, this returns true which is wrong because the value of the property of the nested object go is different.

I have gone through the entire code on paper, line by line and do not know what I have to do, or if the recursive call isn't working to compare the properties rightly or I just don't have the knowledge yet.

Can anyone offer a solution? or and if possible a better way or thinking process to solve this case?

Note: The code only compares object properties and values. It doesn't need to compare constructors or prototypes.

Onome Sotu
  • 666
  • 9
  • 18

1 Answers1

0

There are many techniques for doing deep object comparison, but probably the most reliable one is also the simplest... Using JSON.stringify(obj) on each object to be compared, which turns them into strings and then comparing the strings.

var a = {go: {inside: "yes"}, come: 2, flag: true}
var b = {go: {inside: "yes"}, come: 2, flag: true} 
var c = {go: {inside: "yes"}, come: 1}
var d = {go: {inside: "yes"}, come: 2, flag: "true"} 

function deepEqual(obj1, obj2){
  // Convert both objects to strings and compare them:
  console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); 
}

deepEqual(a,b); // true
deepEqual(b,c); // false
deepEqual(a,d); // false

Now, keep in mind that JSON.stringify() strips out properties that store functions, by default, for security reasons, but using the optional "replacer" argument, you can actually manually persist that into the string.

Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • The problem with this, is that it will consider `{a: 1, b: 1}` different from `{b: 1, a: 1}`, and ignore any nested objects that are not representable in JSON, like `Set`, `Map`, ...etc. – trincot Jul 23 '17 at 18:33
  • @trincot Points taken. – Scott Marcus Jul 23 '17 at 18:34
  • Hello Scott, thanks for this answer, unfortunately, I am not allowed to use abstraction such as the `JSON.stringify()`. – Onome Sotu Jul 23 '17 at 18:36