Bijoy valappil
Bijoy valappil

Reputation: 131

JavaScript - How to reset a local variable during recursion?

There is an object with "value" or/and "children" as properties. The problem is to add all the values and return the sum. This is working. I have used recursion. I am using a global variable to store the sum.

Please refer - https://jsfiddle.net/ws6ty78b/1/

function sumup(node) {
  sum+=node.value;
  if (node.children && node.children.length > 0) {
    for (var i =0; i < node.children.length; i++) {
        sumup(node.children[i]);
    }
}
return sum
}

The problem - While invoking the same function again, I am not getting same result. The sum is doubling. I DO KNOW why it is happening, that is since I am using a global variable.

Question - For the above problem, is there a way I get the same result for any number of invocations.

Please note the restrictions -

1) Do not change the function signature and declaration.

2) Use the 'sum' variable inside the 'sumup' function.

3) Do not use any extra variable.

4) Only make changes within the sumup function.

Upvotes: 2

Views: 1270

Answers (3)

CertainPerformance
CertainPerformance

Reputation: 370929

You could call sumup while recursing and assign/test against a custom this:

var obj = {
  "value": 4,
  "children": [{
      "value": 2,
      "children": [{
        "value": 1
      }]
    },
    {
      "value": 9
    }
  ]
};
var sum = 0;

function sumup(node) {
  //Only make change within this function body
  if (!this || !this.recurse) sum = 0;
  sum += node.value;
  if (node.children && node.children.length > 0) {
    for (var i = 0; i < node.children.length; i++) {
      sumup.call({ recurse: true }, node.children[i]);
    }
  }
  return sum
}

console.log(sumup(obj));
console.log(sumup(obj));

Alternatively, you could do away with the global variable entirely and use a recursive reduce over the children instead:

var obj = {
  "value": 4,
  "children": [{
      "value": 2,
      "children": [{
        "value": 1
      }]
    },
    {
      "value": 9
    }
  ]
};

const sumup = (node) => (
  node.value + (node.children
    ? node.children.reduce((a, child) => a + sumup(child), 0)
    : 0
  )
);

console.log(sumup(obj));
console.log(sumup(obj));

Upvotes: 0

Attersson
Attersson

Reputation: 4866

In order to accomplish your goal the function sumup() will be invoked N times. We don't know how much this N is, since it depends on the number of recursions for the children nodes. Also, thanks to the restrictions, we can't edit the function signature and cannot write code elsewhere to do pretty anything, not even manually set sum to 0.

Therefore we need a way to distinguish one bunch of invocations from the NEXT bunch, and reset it.

My idea is we set a threshold, use a closure. From the first invocation, in the example, you have 5 seconds of time to keep calling and afterwards it will start anew. This is really all we can do unless another way to distinguish the invocations is provided.

EDIT: What I was trying here is keep all the recursions as a base case and not altering the object. Since OP has accepted that solution, I am adding a node resetter property. If any node has resetter not defined or null or zero or undefined, this will reset the sum. We must assume it is not defined in the started object. As I said this is kind of a weak assumption. By defining it when recursing, current sum is carried over. I will also keep the original time threshold idea for eventual interest.

var obj = {
  "value": 4,
  "children": [
    {
      "value": 2,
      "children": [
        {
          "value": 1
        }
      ]
    },
    {
      "value": 9
    }
  ]
}


const sumup = (function(){
  var lastTime = 0;
  var newTime = 0;
  var sum = 0;
  const timeThreshold = 5000; // 5 seconds
  return function(node) {
    newTime = new Date().getTime();
    if(!node["resetter"] || (newTime-lastTime >= timeThreshold)){
      sum=0;
      lastTime = newTime;
      }
      sum+=node.value;
      if (node.children && node.children.length > 0) {
        for (var i =0; i < node.children.length; i++) {
          sumup(Object.assign({"resetter":true},node.children[i]));
        }
      }
      return sum;
   }
})();

console.log(sumup(obj)); //16
console.log(sumup(obj)); //32! should print 16 everytime

Upvotes: 0

Luca Iachini
Luca Iachini

Reputation: 21

You could simply sum the values of each child to the value of the current node:

function sumup(node) {
      sum=node.value;
      if (node.children && node.children.length > 0) {
        for (var i =0; i < node.children.length; i++) {
          sum+=sumup(node.children[i]);
        }
      }
      return sum
    }

Upvotes: 2

Related Questions