0

I have multiple if statements that are being called upon. I want to set it so that if the user cancels all 4 prompts it will prompt them that it's invalid and return them to the beginning of the function? I tried to set it as an if statement, but could not quite get it to work.
I am kind of new to JavaScript so please bear with me or keep it simple.

// Get references to the #generate element
var generateBtn = document.querySelector("#generate");

const myArrayUpper = Array.from(Array(26)).map((e, i) => i + 65);
const alphabetUpper = myArrayUpper.map((x) => String.fromCharCode(x));

const myArrayLower = Array.from(Array(26)).map((e, i) => i + 97);
const alphabetLower = myArrayLower.map((x) => String.fromCharCode(x));

const arrayNumeric = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];

const arraySpecialCharacters = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')'];

function generatePassword() {
  var results = "";
  var numberOfCharacters = window.prompt("How many characters would you like your password to contain");
  var characterQuantity = parseInt(numberOfCharacters);
  if (characterQuantity >= 8 && characterQuantity <= 128) {
    var lowerCase = window.confirm("click OK to confirm lowercase letter.");
    var upperCase = window.confirm("Click OK to confirm uppercase letter.");
    var numeric = window.confirm("Click OK to confirm numeric values");
    var specialCharacters = window.confirm("Click OK to confirm special characters");
    var okayButton = [];
    if (upperCase == true) okayButton.push(alphabetUpper);
    if (lowerCase == true) okayButton.push(alphabetLower);
    if (numeric == true) okayButton.push(arrayNumeric);
    if (specialCharacters == true) okayButton.push(arraySpecialCharacters);


    for (var i = 0; i < characterQuantity; i++) {
      var storeButton = Math.floor(Math.random() * okayButton.length);
      var selectedArray = okayButton[storeButton];
      results += selectedArray[Math.floor(Math.random() * selectedArray.length)];
      // results += alphabetLower[Math.floor(Math.random() *26)];
      // results += arrayNumeric[Math.floor(Math.random() *10)];
      // results += arraySpecialCharacters[Math.floor(Math.random() *10)];
    }
  } else {
    window.alert('This is an invalid entry. Select an entry between 8 and 128');
    return generatePassword();

  }
  return results;
}

// challenge make it so that if they hit cancel to many times instead of error have it prompt them to do it again

// Write password to the #password input
function writePassword() {
  var password = generatePassword();
  var passwordText = document.querySelector("#password");

  passwordText.value = password;

}

// Add event listener to generate button
generateBtn.addEventListener("click", writePassword);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>Password Generator</title>
  <link rel="stylesheet" href="style.css" />
</head>

<body>
  <div class="wrapper">
    <header>
      <h1>Password Generator</h1>
    </header>
    <div class="card">
      <div class="card-header">
        <h2>Generate a Password</h2>
      </div>
      <div class="card-body">
        <textarea readonly id="password" placeholder="Your Secure Password" aria-label="Generated Password"></textarea>
      </div>
      <div class="card-footer">
        <button id="generate" class="btn">Generate Password</button>
      </div>
    </div>
  </div>
  <script src="script.js"></script>
</body>

</html>

4
  • Why do you use Array.from() in Array.from(Array(26))? Array(26) is guaranteed to return an array. Commented Mar 6, 2022 at 5:11
  • Do i not need that Array.from? Commented Mar 6, 2022 at 15:57
  • No, you don't need it. Array.from() is for converting something that's array-like (e.g. a NodeList) to an actual array. Commented Mar 6, 2022 at 18:57
  • Actually, Array.from is necessary because, per the docs, "[map] is invoked only for indexes of the array which have assigned values." Array(26) creates an array of length 26 containing only empty items, so Array(26).map(...) would do nothing. It's worth noting that this is very javascript-specific. In most languages, creating an array will allocate it, even if the values in the resultant array are just whatever was in already memory at those locations. Commented Mar 6, 2022 at 22:09

5 Answers 5

2

Put a loop around the code that asks these questions. If they answer confirm at least one of them, break out of the loop.

There's also little need for variables like upperCase and lowerCase. Just test the confirm() call directly.

// Get references to the #generate element
var generateBtn = document.querySelector("#generate");

const myArrayUpper = Array.from(Array(26)).map((e, i) => i + 65);
const alphabetUpper = myArrayUpper.map((x) => String.fromCharCode(x));

const myArrayLower = Array.from(Array(26)).map((e, i) => i + 97);
const alphabetLower = myArrayLower.map((x) => String.fromCharCode(x));

const arrayNumeric = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];

const arraySpecialCharacters = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')'];

function generatePassword() {
  var results = "";
  var numberOfCharacters = window.prompt("How many characters would you like your password to contain");
  var characterQuantity = parseInt(numberOfCharacters);
  if (characterQuantity >= 8 && characterQuantity <= 128) {
    var okayButton = [];
    while (true) {
      if (window.confirm("click OK to confirm lowercase letter.")) {
        okayButton.push(alphabetLower);
      }
      if (window.confirm("Click OK to confirm uppercase letter.")) {
        okayButton.push(alphabetUpper);
      }
      if (window.confirm("Click OK to confirm numeric values")) {
        okayButton.push(arrayNumeric);
      }
      if (window.confirm("Click OK to confirm special characters")) {
        okayButton.push(arraySpecialCharacters);
      }
      if (okayButton.length > 0) {
        break;
      }
      alert("You need to confirm at least one kind of character, try again.");
    }

    for (var i = 0; i < characterQuantity; i++) {
      var storeButton = Math.floor(Math.random() * okayButton.length);
      var selectedArray = okayButton[storeButton];
      results += selectedArray[Math.floor(Math.random() * selectedArray.length)];
      // results += alphabetLower[Math.floor(Math.random() *26)];
      // results += arrayNumeric[Math.floor(Math.random() *10)];
      // results += arraySpecialCharacters[Math.floor(Math.random() *10)];
    }
  } else {
    window.alert('This is an invalid entry. Select an entry between 8 and 128');
    return generatePassword();

  }
  return results;
}

// challenge make it so that if they hit cancel to many times instead of error have it prompt them to do it again

// Write password to the #password input
function writePassword() {
  var password = generatePassword();
  var passwordText = document.querySelector("#password");

  passwordText.value = password;

}

// Add event listener to generate button
generateBtn.addEventListener("click", writePassword);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>Password Generator</title>
  <link rel="stylesheet" href="style.css" />
</head>

<body>
  <div class="wrapper">
    <header>
      <h1>Password Generator</h1>
    </header>
    <div class="card">
      <div class="card-header">
        <h2>Generate a Password</h2>
      </div>
      <div class="card-body">
        <textarea readonly id="password" placeholder="Your Secure Password" aria-label="Generated Password"></textarea>
      </div>
      <div class="card-footer">
        <button id="generate" class="btn">Generate Password</button>
      </div>
    </div>
  </div>
  <script src="script.js"></script>
</body>

</html>

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

Comments

2

For instructional purposes, here's a refactoring:

const possibleCharacters = [
  ...confirm('Numeric?') && '0123456789' || [],
  ...confirm('Lower case?') && 'abcdefghijklmnopqrstuvwxyz' || [],
  ...confirm('Upper case?') && 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' || [],
  ...confirm('Special?') && '!@#$%^&*()' || []
];

alert('Your password:\n' + 
  [...crypto.getRandomValues(
    new Uint32Array(
      Number.parseInt(prompt('Character count?'))
    )
  )].map(i => possibleCharacters[i % possibleCharacters.length]).join('')
);

That's all there is to it!

An explanation:

Short circuit evaluation

...confirm('Numeric?') && '0123456789' || []

Let's look at just the boolean portion of this line and see how it looks to the JavaScript interpreter.

<true or false> AND truthy-value-a OR truthy-value-b

That is, confirm() will return true or false. The string '0123456789' is truthy, just all non-empty strings are. The empty Array is truthy, just as all Objects are.

When evaluating the false AND true, the interpreter knows right away that the statement is false, because the first item is false. The next item is never evaluated for truthiness. What matters more though is that false is now effectively the return value of that statement. If the first item were true and the next item is truthy, that next item is the return value.

So, true && 'some string' evaluates to 'some string'. That means that if the user confirms the dialog, the dialog item is true, and the string is returned.

If the user does not confirm the dialog, it's false, the string is never evaluated (due to the AND &&), and we go on to the OR ||. false OR true evaluates to true. false OR some-truthy-value evaluates to some-truthy-value. Since the empty array is truthy, it is returned. The reason why we always must return something iterable here will be apparent at this next section.

Spread operator

The ellipsis ... is the Spread Operator in JavaScript. If we wanted to make an array of arrays...

[
  ...[1, 2, 3],
  ...[4, 5, 6]
] // Basically the same as [1, 2, 3, 4, 5, 6]

The cool thing though is that strings are iterable too:

[...'asdf'] // Synonymous with ['a', 's', 'd', 'f']

So above, for each character set, we either iterate over a string of possible characters, or an empty array. That all gets merged into the one big array of possibleCharacters.

Crypto for random values

[...crypto.getRandomValues(
  new Uint32Array(
    Number.parseInt(prompt('Character count?'))
  )
)]

Here, we prompt the user for a character count. Using the response from that, we create a new TypedArray to store some large 32-bit unsigned integers. We populate that array using crypto.getRandomValues(), and the return value of that function is our TypedArray.

Why crypto? It's probably overkill for this use, but it is convenient for returning securely random numbers. Other random number generators are more predictable, in theory and in specific conditions.

So, if we have an array of numbers, why do we need the outer spread [... ] operator and array? This is just a quick way to get a non-typed regular Array from the TypedArray. That matters because we're about to .map() the array to our password in the next step.

Array.map()

This one, you've probably seen before. It's pretty simple. For every item in the array, run the callback function and return a new array with the return values of all those function calls.

Here, we're mapping the big random numbers into characters from our possibleCharacters array. How?...

Modulo operator (%)

If you divide a number by another number but only want integer results, what's the remainder? You can find that via modulous.

0 % 2 // 0
1 % 2 // 1
2 % 2 // 0
3 % 2 // 1
4 % 2 // 0
// etc.

So, how does that help us? Well, some of these random numbers are really big, like 2596003863 or 3979143875. We can't get the 985,031,522nd item of an array with less than 100 items in it. However, with the modulo operator, we can keep the randomness while also keeping it within our array. Think of it like a gameshow wheel where the contestant gives it a really hard pull. The wheel may spin around many times, but it still lands on a random spot.

2616031673 % 72 // 17
3016112995 % 72 // 19
874672118 % 72 // 62
// etc.

Array.join()

Finally, while we'll have an array of characters, we need to shove them all back together as a string. This is where .join() comes in. Whatever is the first parameter is the string inserted between each item of the array. If you use an empty string like we are here, all the characters just go together.

I hope you (or somebody) finds the explanation of these items helpful!

Comments

1

Your question is a bit hard to follow, but I'm going to try to respond to 1) the title, and then 2) what I think you meant by it based on the code.

  1. "How [can one] return a method in JavaScript?" When you wrote return generatePassword();, you returned the result of calling generatePassword. However, you just as easily could have returned generatePassword itself, something that is common practice in a language like javascript, by writing return generatePassword;. Then, the caller could run the returned function as appropriate. Javascript has first class functions, meaning functions can be treated just like any other variable. The term "method" is generally reserved for functions which are members of a class, so I've stuck with just "function" in this description.
  2. I think you're asking about how to re-run the series of prompts when certain conditions are not met. Other users have suggested that a loop is the best way to approach this, and I agree. However, it is also possible to say something like if (conditions not met) { return generatePassword(); }, just as you did in the else branch of your length check. Recursion can be expensive compared to looping, so again, loops are (very broadly speaking) preferred, but your inclination to simply call generatePassword again is not incorrect.

2 Comments

This makes a lot of sense. I was able to play with it and make it work using a different approach than any of the suggested. However, your saying in the parenthesis above i put conditions not met or the 4 different conditions?
When I said "conditions not met", I was using pseudocode. More concretely, you would use your four different conditions, i.e. "conditions met" means upperCase || lowerCase || numeric || specialCharacters, so "not conditions met" means !(upperCase || lowerCase || numeric || specialCharacters).
1

If I understand your question correctly, it should be sufficient to just nest the four ifs in a while loop?

Initialize them as false, and if they all remain false at the end of the user being asked to select one of the options to make them true, then you restart the loop.

if(characterQuantity >= 8 && characterQuantity <= 128) {
        var lowerCase = false
        var upperCase = false
        var numeric = false
        var specialCharacters = false
        while (!lowerCase && !upperCase && !numeric && !specialCharacters) {
            lowerCase = window.confirm("click OK to confirm lowercase letter."); 
            upperCase = window.confirm("Click OK to confirm uppercase letter.");
            numeric = window.confirm("Click OK to confirm numeric values");
            specialCharacters = window.confirm("Click OK to confirm special characters");
            if (!lowerCase && !upperCase && !numeric && !specialCharacters) window.alert("It is not valid to not select any of these. Try again")
        }

Edit: I think you could use an object literal to make the code a bit cleaner, but that's an augmentation really, not strictly necessary to solve your problem.

2 Comments

I am still learning I am unsure what an object literal is, but i understand the snipped provided above
An object literal is a way of encapsulating data so as to make it tidier. Essentially, I think @Kenneth Mano is suggesting that four separate but related variables might be dealt with more cleanly by encapsulating them in a single object. I'm not sure I see any advantage in this particular case, but whenever you are passing complex data around between different bits of code, encapsulation is an essential tool.
0

So one way i made it work is just set an if statement for if none of the values are true using this below:

 if (upperCase || lowerCase || numeric || specialCharacters) {

            for (var i = 0; i < characterQuantity; i++) {
                var storeButton = Math.floor(Math.random() * okayButton.length);
                var selectedArray = okayButton[storeButton];
                results += selectedArray[Math.floor(Math.random() * selectedArray.length)];
            }
        } else {
            window.alert('This is an invalid entry. Select at least ONE to proceed');
            return generatePassword();

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.