1

I'm trying to come up with a self-contained pseudo-random number generator function which will not repeat the previous output.

The obvious not self contained solution would be:

let lastX = -1;

function uniqueNumber(range) {
    let x = Math.round(Math.random() * range - 0.5);
    if (lastX == x) return uniqueNumber(range);
    else {
        lastX = x;
        return x;
    }
}

for (let i = 0; i < 10; i++) {
    console.log(uniqueNumber(5))
}

However I'd like the function to be self contained and having the variable outside of the function seems rough. We could encapsulate the variable and function into a object but I'm trying to come up with a self contained function.

I've came up with a solution using the browser storage as a pseudo memory, but it feels a bit overkill using long term storage as temporary memory:

function uniqueNumber(range) {
    let lastX

    if(localStorage.getItem('numberwang') === null) lastX = -1;
    else lastX = localStorage.getItem('numberwang')

    let x = Math.round(Math.random() * range - 0.5);

    if (lastX == x) return uniqueNumber(range);
    else {
        localStorage.setItem('numberwang', x)
        lastX = x;
        return x;
    }
}

for (let i = 0; i < 10; i++) {
    console.log(uniqueNumber(5))
}

Is there a more subtle way to achieve the same outcome, like possibly saving the last result to a RAM address?

2 Answers 2

3

You don't have to use localStorage or some other complicated procedure. You can just encapsulate lastX in a closure to avoid exposing it as a global variable:

const uniqueNumber = (() => {
  let lastX = -1;

  return range => {
    let x;
    
    do {
      x = Math.round(Math.random() * range - 0.5);
    } while (lastX === x);

    lastX = x;
    return x;
  };
})();

for (let i = 0; i < 10; i++) {
  console.log(uniqueNumber(5))
}

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

3 Comments

Thanks, that seems a lot more sensible. I've got a few questions regarding the answer if you don't mind sparing a moment. Apologies if it's dumb but I've only been doing JS for a few weeks, and those nested arrow functions have went a little over my head. What's happening on lines 1 - 4 to allow the lastX constant to stay instantiated after the function has been completed? Also why are we placing brackets round the entire function? Thanks again :)
The (...) are just a good style to make clear that this is the expression that then gets called by the following (). Functions can access the variables of the function they're inside of. If you do so after the parent finished executing this still applies, and that's what you call a closure.
@JonasWilms the (...) are required for immediately invoked arrow functions or it's a syntax error.
2

The solution to your question (about keeping things "self-contained" is to ALWAYS pass external entities as parameters. This is essentially the same thing as closing over them, but passing things as parameters can be beneficial for testing purposes.

const createUniqueNumberGenerator = lastX => uniqueNumber = range => {
 // your existing code
}

const myGenerator = createUniqueNumberGenerator();
const rand = myGenerator(10);

1 Comment

Thanks! I've had a play around with that code and got it working! :)

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.