1

I'm trying to create a function that will take an integer (0-5) and generate a new integer (0-5) with any value other than n. Here's my approach:

function getNewRand(oldRand, max) {
  do {
    newRand = Math.floor(Math.random() * (max + 1));
  }
  while (newRand == oldRand);
  return newRand;
}

My idea is that the do…while loop should generate numbers until it spits out a number where newRand does not equal oldRand, then break and return that last-generated value.

But this code is not reliably returning a non-n value for n.

Edit: I don't require perfect distribution or "drawing from a deck" discarding of used options, just a new choice from the 5 unselected buttons of the 6.

Full code:

//window.onload = randomizeCat()
var radioChoices = 5 // the number of radio buttons
var array = document.getElementsByName('category');
seedRand = Math.floor(Math.random() * radioChoices);
array[seedRand].checked = true;

catData = [
  ['Royalty', 'People <em>with</em> crowns'],
  ['Villains', '<strong>Stabbies</strong>'],
  ['Offense', '<span style="color:blue;font-size:46px;">Big Stabbies</span>'],
  ['Equestrians', '<strong>Hexapodal Stabbies</strong>'],
  ['Swabbies', 'Tattoo Stabbies'],
  ['Infernal', 'Pitchfork Stabbies'],
]

document.getElementById('royalty').addEventListener('click', updateIcon);
document.getElementById('assassin').addEventListener('click', updateIcon);
document.getElementById('army').addEventListener('click', updateIcon);
document.getElementById('calvary').addEventListener('click', updateIcon);
document.getElementById('privateer').addEventListener('click', updateIcon);
document.getElementById('devil').addEventListener('click', updateIcon);
document.getElementById('cat_rand_btn').addEventListener('click', randomizeCat);

function updateIcon() {
  const catNum = document.querySelector("input[name=category]:checked").value; //works     
  let svg = document.getElementById('cat_icon'); //works     

  svg.setAttribute("viewBox", (catNum * 24) + " 0 24 24");
  updateLabel(catNum); //works
}

function updateLabel(catNum) {
  document.getElementById('cat_rand_label').textContent = catData[catNum][0]; // Put first value from array. Value only
  document.getElementById('category_info').innerHTML = catData[catNum][1]; // Put 2nd value from array. Allows HTML

}

function randomizeCat() {
  var array = document.getElementsByName('category');
  //  let randomNumber = Math.floor(Math.random() * 5);
  let current = 5;
  let randomNumber = getNewRand(current, 5);
  array[randomNumber].checked = true;
  updateLabel(randomNumber);
  updateIcon(randomNumber);
}


function getNewRand(oldRand, max) {
  do {
    newRand = Math.floor(Math.random() * (max + 1));
  }
  while (newRand == oldRand);
  return newRand;
}
#cat_rand_label {
  font-size: 200%;
}

#cat_icon {
  width: 80px;
  height: 80px;
}

#cat_icon:hover {
  filter: invert(16%) sepia(18%) saturate(6943%) hue-rotate(342deg) brightness(118%) contrast(90%);
}

#cat_rand_btn {
  width: 100px;
  height: 120px;
}
<div class="h_selection">
  <input type="radio" id="royalty" class="cat_Choice" name="category" value="0" />
  <label for="Royalty" onclick="updateCat()">Royalty</label>

  <input type="radio" id="assassin" class="cat_Choice" name="category" value="1" />
  <label for="Assassin" onclick="updateCat()">Assassin</label>

  <input type="radio" id="army" class="cat_Choice" name="category" value="2" />
  <label for="Army" onclick="updateCat()">Army</label>

  <input type="radio" id="calvary" class="cat_Choice" name="category" value="3" />
  <label for="Calvary" onclick="updateCat()">Calvary</label>

  <input type="radio" id="privateer" class="cat_Choice" name="category" value="4" />
  <label for="Privateer" onclick="updateCat()">Privateer</label>

  <input type="radio" id="devil" class="cat_Choice" name="category" value="5" />
  <label for="Devil">Devil</label>

</div>


<div>
  <button id="cat_rand_btn" class="image-text-button shaker" title="Random Category" onclick="randomizeCat()">

    <svg id="cat_icon" class="bright_filter" viewBox="0 0 24 24" width="90%">
      <path d="m68.18 21.78a20.29 20.29 0 0 1 -3.18-1.72l-1.75 2.05-.07-.11a2.12 2.12 0 0 1 .35-2.23c-16.31-10.67-13.68-18.47-13.68-18.47 2.37 4.6 5 3.13 5 3.13 0 6.43 7.88 11.89 10.11 13.67l1.29-1.7v.12a3 3 0 0 1 -.5 2.64 15.66 15.66 0 0 0 3 1.63" />
      <path d="m39.1 10.65a5.35 5.35 0 0 0 -2-.7c0-.93 0-2.08 0-3.51 0-1.8 1.08-2.38-.93-2.38s-.9.58-.9 2.38v3.51a5.31 5.31 0 0 0 -2 .7l-.4.26.5.79.4-.25a3.61 3.61 0 0 1 1-.41c-.07 1.62-.27 8.73 1.43 11.2 1.64-2.49 1.49-9.59 1.43-11.21a4 4 0 0 1 1 .42l.4.25.51-.79z" />
      <path d="m32.18 9.09a5.17 5.17 0 0 0 -1.86-1c.15-.92.34-2 .56-3.47.28-1.77 1.44-2.18-.54-2.49s-1 .43-1.27 2.21c-.22 1.42-.4 2.55-.53 3.47a5.13 5.13 0 0 0 -2.08.38l-.46.18.38.87.43-.19a3.66 3.66 0 0 1 1-.26c-.32 1.59-1.63 8.59-.34 11.29 2-2.21 3-9.24 3.17-10.84a3.3 3.3 0 0 1 .88.56l.36.31.62-.71z" />
      <path d="m46 8.18a5.21 5.21 0 0 0 -2.09-.38c-.13-.92-.31-2-.53-3.47-.28-1.78.71-2.53-1.27-2.21s-.82.72-.54 2.49c.23 1.42.41 2.55.57 3.47a5 5 0 0 0 -1.86 1l-.36.31.63.71.35-.31a3.3 3.3 0 0 1 .88-.56c.2 1.6 1.16 8.63 3.17 10.84 1.29-2.7 0-9.7-.34-11.29a3.66 3.66 0 0 1 1 .26l.43.19.38-.87z" />
      <path d="m23.51 7.2a2.44 2.44 0 0 1 -2.51.67c.2 2.25-1.73 5.69-2.9 5.22s-.9-5.5 1.3-6.48a3.52 3.52 0 0 1 -1.51-2.54 3 3 0 0 1 -2.3 1.41c1.13 1.63 0 6.93-1.66 6.34-1.22-.43-1.62-5.44 0-7.44a3.61 3.61 0 0 1 -2-2.21 3.62 3.62 0 0 1 -1.93 2.21c1.6 2 1.21 7 0 7.44-1.7.59-2.79-4.71-1.66-6.34a3 3 0 0 1 -2.34-1.41 3.53 3.53 0 0 1 -1.5 2.54c2.2 1 2.33 6.06 1.29 6.48s-3.1-3-2.89-5.22a2.42 2.42 0 0 1 -2.54-.67l4 11.46a.48.48 0 0 0 -.14.31v1c0 .92 3.43 1.66 7.67 1.66s7.68-.74 7.68-1.66v-.97a.45.45 0 0 0 -.15-.31zm-11.58 13.71c-3.7 0-6.7-.65-6.7-1.45s3-1.44 6.7-1.44 6.71.64 6.71 1.44-3.01 1.45-6.71 1.45z" />
      <path d="m94.2 10.77-4.2-3.43a.24.24 0 0 1 0-.12c-.08-.27-.16-.55-.23-.84a1.88 1.88 0 0 0 -1.06-1.27l-2.71-1.28a.13.13 0 0 1 -.09-.15v-.37c.09-.61.17-1.22.27-1.83a.4.4 0 0 0 -.47-.48 3.61 3.61 0 0 0 -1.71.94 5.23 5.23 0 0 0 -1.36 2.06.18.18 0 0 1 0 .07.09.09 0 0 1 -.06 0l-.39.13a13.64 13.64 0 0 0 -7.19 18.62h2.6a11.7 11.7 0 0 1 4.68-17.17 10.24 10.24 0 0 0 -5.82 10 11.61 11.61 0 0 0 3 7.07l10.95.08h.08a.26.26 0 0 0 .25-.32c0-.1 0-.19 0-.29a6.43 6.43 0 0 0 -1.58-3.19 15.4 15.4 0 0 0 -1.85-1.76 7.28 7.28 0 0 1 -1.07-1 2.22 2.22 0 0 1 -.56-1.62 1.93 1.93 0 0 1 .58-1.14 3 3 0 0 0 1.59.41 4.92 4.92 0 0 0 1.85-.4.14.14 0 0 1 .14 0l1.76 1.16a1.51 1.51 0 0 0 2.1-.49c.26-.46.52-.93.79-1.38a1.5 1.5 0 0 0 -.29-2.01zm-7.57-3.43a5 5 0 0 1 2.8.62c-1.56 2.5-2.8-.62-2.8-.62z" />
      <circle cx="69.07" cy="21.6" r="1.07" />
      <path d="m51.66 21.78a20.29 20.29 0 0 0 3.19-1.72l1.75 2.05.06-.15a2.12 2.12 0 0 0 -.35-2.23c16.31-10.63 13.69-18.43 13.69-18.43-2.37 4.6-5 3.13-5 3.13 0 6.43-7.89 11.89-10.11 13.67l-1.29-1.7v.12a3 3 0 0 0 .5 2.64 16 16 0 0 1 -3 1.63" />
      <circle cx="50.77" cy="21.6" r="1.07" />
      <path d="m117.47 13.87a6.9 6.9 0 0 0 -2.1-3.4 30.28 30.28 0 0 1 -1.3 3.58c-.09.16.09.33.27.27l.18-.07a.22.22 0 0 1 .25.07c-.1.44-.23.88-.37 1.31a.61.61 0 0 0 -.05.12c-.58 1-1.79 2.14-4.36 1.61l-.62-8.24 4.08-.55v-.82l-4.23-.57-.1-1.33h.09v-1.85h-.21v-.08h-1.5v.08h-.23v1.85h.09l-.1 1.33-4.26.57v.82l4.08.55-.62 8.24c-2.57.53-3.78-.57-4.36-1.61a.61.61 0 0 0 0-.12c-.14-.43-.27-.87-.37-1.31a.21.21 0 0 1 .24-.07l.19.07c.18.06.36-.11.27-.27a30.28 30.28 0 0 1 -1.3-3.58 6.9 6.9 0 0 0 -2.1 3.4.22.22 0 0 0 .27.23l.47-.12a.23.23 0 0 1 .28.2 6.32 6.32 0 0 0 4.17 6.28c4 1.37 4.09 2.57 4.09 2.66 0-.08.09-1.24 4.09-2.66a6.37 6.37 0 0 0 4.17-6.28.23.23 0 0 1 .28-.2l.47.12a.22.22 0 0 0 .15-.23z" />
      <circle cx="108.19" cy="2.93" fill="none" r="1.69" stroke="#000" stroke-miterlimit="10" stroke-width=".999014" />
      <path d="m139.34 7.85c.75-1.09 1.16-2.73.18-5.12a7.86 7.86 0 0 1 -1.15 2.7 9 9 0 0 0 -1.54-2.06 7.05 7.05 0 0 0 -9.61 0 8.47 8.47 0 0 0 -1.54 2.06 7.76 7.76 0 0 1 -1.16-2.7c-1 2.39-.56 4 .18 5.12 0 .18-3.92 10.23.36 11.83.9.34 4.67 1.06 7 3.78 2.28-2.72 6.06-3.44 7-3.78 4.16-1.6.37-11.53.28-11.83zm-8.64 5.15-1.8.26a1.57 1.57 0 0 1 -1.65-1.46v-.3l1.75-.3a1.57 1.57 0 0 1 1.66 1.46zm2.67-.29a1.57 1.57 0 0 1 1.63-1.51l1.8.25v.3a1.58 1.58 0 0 1 -1.66 1.46l-1.8-.26zm-1.78-3.88a5 5 0 0 0 -4.07-2.28 2.09 2.09 0 0 1 -1.38-.56 7.74 7.74 0 0 1 1.49-2 6.7 6.7 0 0 1 8.78 0 7.52 7.52 0 0 1 1.49 2 2.09 2.09 0 0 1 -1.38.56 5 5 0 0 0 -4.08 2.28.5.5 0 0 1 -.85-.05z" />
      <g fill="none">
        <path d="m120 0h24v24h-24z" />
        <path d="m96 0h24v24h-24z" />
        <path d="m72 0h24v24h-24z" />
        <path d="m48 0h24v24h-24z" />
        <path d="m24 0h24v24h-24z" />
        <path d="m0 0h24v24h-24z" />
      </g>
    </svg>
    <span id="cat_rand_label">Royalty</span>
  </button>
  <div id="category_info">Descripton of Category</div>
</div>
Oxidized
  • 15
  • 5
  • 1
    Well... That's one way, but you'll find as you start running out of numbers, it may take a while to generate a new "random" number from the remaining numbers. It's better to store "available" numbers within a list, generate a random number between 0 and `sizeOfList - 1`, and just use that to index into the list of remaining numbers. Otherwise, you aren't generating numbers in O(n) and it gets messy. If you are okay with reusing numbers but you just don't want the last number, what you have seems reasonable. – h0r53 Sep 07 '21 at 17:57
  • 2
    You're always using `current = 5`. – Barmar Sep 07 '21 at 17:58
  • Sounds almost exactly like [this answer I gave yesterday](https://stackoverflow.com/a/69074930/295783) – mplungjan Sep 07 '21 at 18:04

3 Answers3

0

You don't have to worry about do-while conditions or breaks. You can keep generating your random number with a simple while loop and compare the generated number against oldRand, if they are different return newRand.

function getNewRand(oldRand, max) {
  while (true) {
    newRand = Math.floor(Math.random() * (max + 1));
    if (oldRand !== newRand) {
      return newRand;
    }
  }
}
Atif
  • 10,623
  • 20
  • 63
  • 96
0

If the only number you need to skip is oldRand, you could shorten the size of the range by one, and if the generated number is equal or greater to oldRand, add one to it:

function getNewRand(oldRand, max) {
  newRand = Math.floor(Math.random() * max);
  if (newRand >= oldRand) {
    newRand += 1;
  }
  return newRand;
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • Doesn't this create an uneven distribution of numbers though? For example, using this technique you will never reach the "max" number in the available range, unless it just so happens that the "max" number is `oldRand + 1` – h0r53 Sep 07 '21 at 18:01
  • I think the function is supposed to iterate until a number is produced that doesn't match oldRand – Atif Sep 07 '21 at 18:01
  • @h0r53 no. `newRand` is an evenly distributed random number in the range [0, max - 1]. Then, assuming that `oldRand` is in the range, we add one, so you get a split range `[0, oldRand - 1], [oldRand + 1, max]`, with an even chance to each result – Mureinik Sep 07 '21 at 18:29
  • 1
    @Atif the function is supposed to produce a random number that isn't `oldRand` - I don't think there's any requirement to use a loop - it's just an implementation detail, and a questionable one at that. This implementation is more efficient. – Mureinik Sep 07 '21 at 18:30
  • 1
    @Mureinik You! I like the way you think. I'm still baffled and have the same question as h0r53 over and over but I will take some time to understand how it works. Thanks – Atif Sep 07 '21 at 18:36
  • @Mureinik This logic makes sense to me, but it's still "hanging" on a particular choice at intervals. Perhaps the problem isn't with my getNewRand function. – Oxidized Sep 07 '21 at 18:47
0

You can also try a Recursive approach, it checks if the new Random value is equal to the previous, then calls the same function again when the condition is true.

function getNewRand(oldRand, max) {
  const newRand = Math.floor(Math.random() * (max + 1));
  return (newRand === oldRand ? getNewRand(newRand, max): newRand)
}

const oldRand = 3;
const max = 5;

const newRandom = getNewRand(oldRand, max);
console.log(newRandom)
ahsan
  • 1,409
  • 8
  • 11
  • This works but it could be troublesome if someone gave input such as `getNewRand(0,0)`. Though this is arguably the _caller's fault_, I lean towards not writing functions with the potential for misuse when an alternative exists. – h0r53 Sep 07 '21 at 18:05
  • @h0r53 that same could be an issue with other answers having a `while` loop approach, corner case like these should be catered separately. – ahsan Sep 07 '21 at 18:14
  • This would be overkill for my use case, as I don't need to discard any choice but the one immediately preceding. – Oxidized Sep 07 '21 at 18:32