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

javascript recursive function array
recursive function javascript example
recursion
implementation of recursion
recursion and stack in c
recursion java
return value from recursive function javascript
recursive function in javascript mdn

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}));

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;
  }
  for (var k in o2){
    if (!(k in o1))
      return false;
  }
  return true;
}

Recursion in Functional JavaScript, You may have come across references to recursive functions while programming easy to write in a pure manner, with a specific and consistent return value for any given input, and no side effects on external variable states. Now, each recursive call is replaced by a set of instructions which dothe following: (iv) Store the values of all pass by valueparametersand local variables in the stack. The pointer to the top of the stackcanbe treated as global. (v) Create the i-th new label, i, and 'store i inthe stack.

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);

Reading 14: Recursion, Safe from bugs, Easy to understand, Ready for change A recursive function is defined in terms of base cases and recursive steps. and local variables, and doesn't use static variables or global variables, and doesn't share and the recursive methods are all pure functions in the sense that they do not mutate anything. For example, the following procedure uses a recursive function to calculate factorials. Function Factorial (N) If N <= 1 Then ' Reached end of recursive calls. Factorial = 1 ' (N = 0) so climb back out of calls.

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

Functional C#, To convert the preceding two examples of non-pure function code into pure function code, In the AddSpace function, instead of modifying the global variable or argument, because the function does not depend on external state and variables. Recursive functions In an imperative programming world, we have got [20]  A recursive function is defined in terms of base cases and recursive steps. In a base case, we compute the result immediately given the inputs to the function call. In a recursive step, we compute the result with the help of one or more recursive calls to this same function, but with the inputs somehow reduced in size or complexity, closer to a

function deepEqual(object1, object2) {
  //check if the given objects have the same datatype
  if (typeof(object1) === typeof(object2)) {
    //check if the given object is a primitive datatype and how to handle null values
    if ((typeof(object1) !== 'object') && (typeof(object2) !== 'object') ||
      object1 === null || object2 === null) {
      return object1 === object2;
    } else {
      //if they are both objects
      if (object1 !== null && object2 !== null) {
        let object1Keys = Object.keys(object1);
        let object2Keys = Object.keys(object2);

        //check if the arrays have the same length
        if (object1Keys.length === object2Keys.length) {
          let isEqual;
          for (let index = 0; index < object1Keys.length; index++) {
            //make sure both key:value pairs match
            if (object1Keys[index] === object2Keys[index]) {
              //check if the current value is another object
              if (typeof(object1[object1Keys[index]]) === 'object' &&
                typeof(object2[object2Keys[index]]) === 'object') {
                return deepEqual(object1[object1Keys[index]], object2[object2Keys[index]]);
              } else {

                isEqual = (object1[object1Keys[index]] === object2[object2Keys[index]]);
              }
            } else {

              return false; //return false if keys dont match
            }
          }
          return isEqual;

        } else {
          return false; //return false if 2 arrays dont have the same length
        }

      }
    }
  } else {
    return false; //return false if 2 object types dont match
  }
}

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

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


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

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

console.log(deepEqual(obj3, obj4));
let obj = {
  name: {
    gender: "F"
  },
  age: 20
};

console.log(deepEqual(obj, {
  name: {
    gender: "F"
  },
  age: 20
}));
console.log('null == obj3');
console.log(deepEqual(null, obj3));
console.log('5 == obj3');
console.log(deepEqual(5, obj3));
console.log('null == null');
console.log(deepEqual(null, null));
console.log('10 == 5');
console.log(deepEqual(10, 5));
console.log(`10 == '10'`);
console.log(deepEqual(10, '10'));

From Impure to Pure Code, Instead of referring to global variables inside a function, pass the global practical use by itself, but it's an elementary building block used to turn loops pure, a recursive function loop that accepts all loop variables as arguments, starting with  One approach that can be very useful is to write functions that are locally impure (that is, they declare and modify local variables) but are globally pure. Such functions have many of the desirable composability characteristics, but avoid some of the more convoluted functional programming idioms, such as having to use recursion when a simple

Lambda Calculus with Types, Every computable (recursive) function is lambdadefinable in the M-calculus (see Church LISP is not a pure functional language for several reasons. review and avoided confusing local and global variables by introducing a-conversion. In line 4, a and b are declared as two global variables of type int.The variable a will be automatically initialized to 0. You can use variables a and b inside any function. . Notice that inside function func_2() there is a local variable with the same name as a global variab

Functional Programming in JavaScript, Functional programs share a variety of common helper functions, due to the functional programmer works to write first-class, higher-order, pure functions that are completely independent of each other with no side effects on global variables​, But modifying one function will not change another, so long as the one-to-one​  When dealing with recursive functions, keep in mind that each recursive call has its own execution context, so to maintain state during recursion you have to either: Thread the state through each recursive call so that the current state is part of the current call’s execution context. Keep the state in global scope.

JavaScript: Functional Programming for JavaScript Developers, Functional programs share a variety of common helper functions, due to the functional programmer works to write first-class, higher-order, pure functions that are completely independent of each other with no side effects on global variables​, But modifying one function will not change another, so long as the one-to-one​  Global variables. Global variables are those variables which exists throughout the execution of a program. It can be changed and accessed from any part of the program. However, global variables also depend upon the perspective of a function. For example, in the above example, from the perspective of inner_func(), both a and b are global variables.

Comments
  • 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. :)
  • recursive diff and recursive union - I think these two Q&As will show you other important things to consider.
  • comparing the the length of keys is not reliable. { a: 1 } and { b: 2 } both have one key but that tells you nothing about equality.
  • Are { a: /foo/ } and { a: /foo/ } considered equal? How about { a: someFunc } and { a: someFunc }? Consider other complex objects like HTMLElement too. You have to ask and answer more questions before your object equality function can be robust and reliable.
  • This is perfect! I did use recursion, however I was trying to carry over the variable value which is what I wanted to know how to not do. Your way is tons better and much less messy :)
  • Looking at it, I think you'd also want to test for o2 being null if o1 isn't, but it's at least a place to start exploring.
  • Yes,absolutely.
  • deepEqual({a:1}, {a:1,b:2}) // true
  • warning: the linked repo is untested
  • @user633183 Does it break any test cases. If yes please share with me i am happy to handle that also.