Allow One Function Call | #2666 | LeetCode Solution

Author: neptune | 11th-Sep-2023

Problem : Allow One Function Call | #2666 | LeetCode

Given a function `fn`, return a new function that is identical to the original function except that it ensures `fn` is called at most once.

  • The first time the returned function is called, it should return the same result as `fn`.

  • Every subsequent time it is called, it should return `undefined`.


Example 1:

Input: fn = (a,b,c) => (a + b + c), calls = [[1,2,3],[2,3,6]]

Output: [{"calls":1,"value":6}]

Explanation:

const onceFn = once(fn);

onceFn(1, 2, 3); // 6

onceFn(2, 3, 6); // undefined, fn was not called

Example 2:

Input: fn = (a,b,c) => (a * b * c), calls = [[5,7,4],[2,3,6],[4,6,8]]

Output: [{"calls":1,"value":140}]

Explanation:

const onceFn = once(fn);

onceFn(5, 7, 4); // 140

onceFn(2, 3, 6); // undefined, fn was not called

onceFn(4, 6, 8); // undefined, fn was not called


Solution:

        /**

     * @param {Function} fn

     * @return {Function}

     */

    var once = function (fn) {


        let hasBeenCalled = false; // Initialize a flag to track whether fn has been called


        return function (...args) {

            if (!hasBeenCalled) {

                // If fn has not been called before

                hasBeenCalled = true; // Set the flag to true to indicate that fn has been called

                return fn(...args); // Call fn with the provided arguments and return its result

            } else {

                // If fn has already been called

                return undefined; // Return undefined as specified

            }

        };

    };


    /**

     * let fn = (a,b,c) => (a + b + c)

     * let onceFn = once(fn)

     *

     * onceFn(1,2,3); // 6

     * onceFn(2,3,6); // returns undefined without calling fn

     */



Explanation:

Let's break down the code step by step:


1. The code defines a function called `once` that takes a single parameter, `fn`, which is expected to be a function.


2. Inside the `once` function, a boolean variable named `hasBeenCalled` is initialized to `false`. This variable will be used to track whether the `fn` function has been called previously.


3. The `once` function returns a new function created using a closure. This new function accepts any number of arguments using the rest operator `...args`.


4. Inside the returned function:

   - It checks if `hasBeenCalled` is `false`, which means that the `fn` function has not been called before.

   - If `hasBeenCalled` is `false`, it sets `hasBeenCalled` to `true` to indicate that the `fn` function has now been called.

   - It then calls the original `fn` function with the provided arguments `...args` and returns its result.


5. If `hasBeenCalled` is `true`, indicating that the `fn` function has already been called previously, the returned function simply returns `undefined`, adhering to the behavior of allowing `fn` to be called at most once.


6. Below the `once` function, there's an example of how to use it:

   - It defines a function `fn` that takes three arguments and returns their sum.

   - It creates a new function `onceFn` by calling `once(fn)`. This new function, `onceFn`, is now capable of ensuring that `fn` is called at most once.


7. It demonstrates the usage of `onceFn`:

   - The first call to `onceFn(1, 2, 3)` returns `6` because it calls the original `fn` function and calculates the sum of the arguments.

   - The second call to `onceFn(2, 3, 6)` returns `undefined` without calling the original `fn` function again, as it has already been called once.


In summary, the `once` function creates a wrapper function that ensures the original function `fn` is called only once. Subsequent calls to the wrapper function return `undefined` to prevent additional calls to `fn`. This is achieved by using a flag (`hasBeenCalled`) to keep track of whether `fn` has been called before.






document.addEventListener("click", function(e) { if (e.target.classList.contains("copy-btn")) { const codeBlock = e.target.closest(".code-block").querySelector("pre").innerText; // ✅ Prefer modern Clipboard API if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(codeBlock).then(() => { e.target.textContent = "✅ Copied"; setTimeout(() => { e.target.textContent = "📋 Copy"; }, 2000); }).catch(err => { console.error("Clipboard write failed:", err); }); } else { // ✅ Fallback for insecure contexts / older browsers const textarea = document.createElement("textarea"); textarea.value = codeBlock; document.body.appendChild(textarea); textarea.select(); try { document.execCommand("copy"); e.target.textContent = "✅ Copied"; setTimeout(() => { e.target.textContent = "📋 Copy"; }, 2000); } catch (err) { console.error("Fallback copy failed:", err); } document.body.removeChild(textarea); } } });