How to convert a recursive function using a global variable in to a pure function?

1050 views javascript
6

I have a program that does a deep compare on 2 objects provided to it and uses recursion to do so. My problem is that since I am using a global variable to retain information, I have to reset it every time prior to making any subsequent calls to the function. Is there someway I can maintain the variable value other than using a global variable and have it be not so cumbersome?

let isEqual = true;
function deepEqual(object1, object2) {

  if (!((typeof(object1) == 'object' && typeof(object2) == 'object') || (object1 && object2))) {

    return isEqual = object1 === object2;

  } else if (typeof(object1) == 'object' && typeof(object2) == 'object') {

    if ((object1 && object2)) {

      let object1Keys = Object.keys(object1);

      let object2Keys = Object.keys(object2);
    
      if (object1Keys.length == object2Keys.length) {
        for (let index = 0; index < object1Keys.length; index++) {
          if (isEqual) {
            if (!(typeof(object1[object1Keys[index]]) == 'object' && typeof(object2[object2Keys[index]]) == 'object')) {
             isEqual = (object1[object1Keys[index]] === object2[object2Keys[index]]) && (object1Keys[index] === object2Keys[index]);
            } else {
              deepEqual(object1[object1Keys[index]], object2[object2Keys[index]]);
            }

          } else {
            return isEqual = false;
          }
        }
      }
    }

  }

  return isEqual;
}

let obj1 = {
  a: 'somestring',
  b: 42,
  c: {
    1: 'one',
    2: {
      4: 'Three'
    }
  }
};

let obj2 = {
  a: 'somestring',
  b: 42,
  c: {
    1: 'one',
    2: {
      3: 'Three'
    }
  }
};
console.log("obj1 == obj2 : ");
console.log(deepEqual(obj1, obj2));


let obj3 = {
  a: 'somestring',
  b: 42,
  c: {
    1: 'one',
    2: {
      3: 'Three'
    }
  }
};

let obj4 = {
  a: 'somestring',
  b: 42,
  c: {
    1: 'one',
    2: {
      3: 'Three'
    }
  }
};
console.log("obj3 == obj4 : ");
isEqual = true;
console.log(deepEqual(obj3, obj4));
let obj = {name: {gender: "F"}, age: 20};
isEqual = true;
console.log(deepEqual(obj, {name: {gender: "F"}, age: 20}));

answered question

JSON.stringify(obj1) === JSON.stringify(obj2) // JSON.stringify(obj3) === JSON.stringify(obj4)

@RandyCasburn Thanks! Is there any way to achieve it via a pure recursive function. I want to learn recursion or rather a good way to implement it. :)

3 Answers

3

I have created an utility just to deep compare two object. It uses the recursive call with two object and return true or false.

Github link for repo - https://github.com/maninder-singh/deep-compare

<script src="deep-compare.js"></script>

JS

    1. dc(null,null);
    2. dc("a","a");
    3. dc("a","ab");
    4. dc("a",undefined);
    5. dc(undefined,undefined);
    6. dc({},[]);
    7. dc({a:1},{});
    8. dc({a:1},{a:1});
    9. dc(true,true);
    10. dc(true,false);

posted this
3

You can use tested, bullet proof Object equality methods provided my various JS library to perform Object equality testing as illustrated below

lodash Library:

_.isEqual(obj1, obj2)

Or
Custom Tested Method

function deepCompare () {
  var i, l, leftChain, rightChain;

  function compare2Objects (x, y) {
    var p;

    // remember that NaN === NaN returns false
    // and isNaN(undefined) returns true
    if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
         return true;
    }

    // Compare primitives and functions.     
    // Check if both arguments link to the same object.
    // Especially useful on the step where we compare prototypes
    if (x === y) {
        return true;
    }

    // Works in case when functions are created in constructor.
    // Comparing dates is a common scenario. Another built-ins?
    // We can even handle functions passed across iframes
    if ((typeof x === 'function' && typeof y === 'function') ||
       (x instanceof Date && y instanceof Date) ||
       (x instanceof RegExp && y instanceof RegExp) ||
       (x instanceof String && y instanceof String) ||
       (x instanceof Number && y instanceof Number)) {
        return x.toString() === y.toString();
    }

    // At last checking prototypes as good as we can
    if (!(x instanceof Object && y instanceof Object)) {
        return false;
    }

    if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
        return false;
    }

    if (x.constructor !== y.constructor) {
        return false;
    }

    if (x.prototype !== y.prototype) {
        return false;
    }

    // Check for infinitive linking loops
    if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
         return false;
    }

    // Quick checking of one object being a subset of another.
    // todo: cache the structure of arguments[0] for performance
    for (p in y) {
        if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
            return false;
        }
        else if (typeof y[p] !== typeof x[p]) {
            return false;
        }
    }

    for (p in x) {
        if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
            return false;
        }
        else if (typeof y[p] !== typeof x[p]) {
            return false;
        }

        switch (typeof (x[p])) {
            case 'object':
            case 'function':

                leftChain.push(x);
                rightChain.push(y);

                if (!compare2Objects (x[p], y[p])) {
                    return false;
                }

                leftChain.pop();
                rightChain.pop();
                break;

            default:
                if (x[p] !== y[p]) {
                    return false;
                }
                break;
        }
    }

    return true;
  }

  if (arguments.length < 1) {
    return true; //Die silently? Don't know how to handle such case, please help...
    // throw "Need two or more arguments to compare";
  }

  for (i = 1, l = arguments.length; i < l; i++) {

      leftChain = []; //Todo: this can be cached
      rightChain = [];

      if (!compare2Objects(arguments[0], arguments[i])) {
          return false;
      }
  }

  return true;
}

Reference: Object comparison in JavaScript

posted this
13

You don't need to use it at all: you can do the whole thing via recursion:

function deepEqual(o1, o2){
  if (typeof o1 != typeof o2)
    return false;

  if (typeof o1 != 'object' || o1 === null || o2 === null)
    return o1 === o2;

  for (var k in o1){
   if (!deepEqual(o1[k], o2[k]))
    return false;
  }
  return true;
}

posted this

Have an answer?

JD

Please login first before posting an answer.