21

I want to write a function once that accepts a callback as input and returns a function. When the returned function is called the first time, it should call the callback and return that output. If it is called any additional times, instead of calling the callback again it will simply return the output value from the first time it was called.

Below is what I have tried to do. But I am not getting the results as I expected. I need to understand this concept.

function once(func) {
    let num; 
    function retFunc(x){
        num = func(x);
        return num;
    }
    return retFunc;
}

function addByTwo(input){
    return input + 2;
}

var onceFunc = once(addByTwo);

console.log(onceFunc(4));  //should log 6
console.log(onceFunc(10));  //should log 6
console.log(onceFunc(9001));  //should log 6
2
  • 1
    The num is already a good first step, but how do you remember that it was called a first time already? You always overwrite num = func(x) Commented Jan 7, 2019 at 7:29
  • FYI, this is called memoization Commented Jan 7, 2019 at 17:14

5 Answers 5

19

You should only assign num = func(x) when num is undefined - that is, on the very first call of retFunc:

function once(func) {
  let num; 
  function retFunc(x){
    if (num === undefined) {
      num = func(x);
    }
    return num;
  }
  return retFunc;
}

function addByTwo(input){
  return input + 2;
}

var onceFunc = once(addByTwo);

console.log(onceFunc(4));  //should log 6
console.log(onceFunc(10));  //should log 6
console.log(onceFunc(9001));  //should log 6

But this isn't a guaranteed general solution - what if the passed function (addByTwo in your example) results in undefined when called? Then, the === undefined check won't work. So, it might be better to set a flag or something similar, and reassign that flag the first time the callback is called:

function once(func) {
  let num;
  let done = false;
  function retFunc(x){
    if (!done) {
      done = true;
      num = func(x);
    }
    return num;
  }
  return retFunc;
}

function returnsUndefinedOn1(input){
  return input === 1 ? undefined : input;
}

var onceFunc = once(returnsUndefinedOn1);

console.log(onceFunc(1));
console.log(onceFunc(10));
console.log(onceFunc(9001));

Sign up to request clarification or add additional context in comments.

6 Comments

That's the desired behavior - on an input of 1, the function returnsUndefinedOn1 returns undefined, per its name. Further calls of onceFunc will also return undefined, despite being called with an argument other than 1.
Please use typeof to compare with undefined
@PierreArlaud In any remotely reasonable code, it shouldn't be necessary, I think? Many global object can be redefined, that doesn't mean they shouldn't be used, that means not to reassign them.
@CertainPerformance typeof also does not trigger an exception if the variable is not declared. There is no scenario where not using typeof to compare with undefined is the better choice. My opinion on the matter is that using typeof is a good habbit to have, and "remotely reasonable codes" are no exceptions.
@PierreArlaud: In this case, getting an exception if the variable is not declared is desirable, since we're trying to test the value of a variable we know we just declared, and the exception is telling us that we made a typo in the variable name and the code is broken.
|
8

You should only call the function and assign num if it's undefined, otherwise you overwrite it every time:

function once(func) {
  let num;

  function retFunc(x) {
    num = (num === undefined) ? func(x) : num
    return num;
  }
  return retFunc;
}

function addByTwo(input) {
  return input + 2;
}

var onceFunc = once(addByTwo);

console.log(onceFunc(4)); //should log 6
console.log(onceFunc(10)); //should log 6
console.log(onceFunc(9001)); //should log 6

Note that if the function you pass in returns undefined it will be called more than once. If you need to handle that case you can set a flag to indicate whether the cached value is valid.

2 Comments

wow, you're really helpful around here aren't ya? no wonder you have 36.8k rep
@YangK mostly insomnia!
3

What you're missing is removing the original function after the first execution. You should modify your code in the following way:

function once(func) {
  let num; 
  function retFunc(x){
      if (func)
          num = func(x);
      func = null;
      return num;
 }
    return retFunc;
}

function addByTwo(input){
  return input + 2;
}

var onceFunc = once(addByTwo);

console.log(onceFunc(4));  //should log 6
console.log(onceFunc(10));  //should log 6
console.log(onceFunc(9001));  //should log 6

This way you remove the function after first usage and you're just keeping the result.

1 Comment

Also i'd like to say that this way is better than relying on the result, because function doesn't need to always return something. It may return undefined, but we still want to run it only once.
2

This is your problem

function retFunc(x){
        num = func(x);
        return num;
    }

You're always calling func(x). Add an if condition to check if num is undefined before calling func(x).

1 Comment

Hi by adding if condition to check whether num is undefined my code is working now.
0

different approach: overwrite the call to func

no flags, check if result is undefined or anything required

function once(func) {
    let num;

    let internalFn = function(x) {
        num = func(x);
        internalFn = function(x) {
            return num;
        }
        return num;
    }

    function retFunc(x){
        return internalFn(x);
   }
      return retFunc;
  }

  function addByTwo(input){
    return input + 2;
  }

  var onceFunc = once(addByTwo);

  console.log(onceFunc(4));  //should log 6
  console.log(onceFunc(10));  //should log 6
  console.log(onceFunc(9001));  //should log 6

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.