Picking a Random Item from an Array

Here is a full example that just focuses on how to call a random function from an array of functions and execute it:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Pick a Random Function</title>
</head>

<body>
  <script>
    function hello(name) {
      console.log(`Hello, ${name}!`);
    }

    function goodBye(name) {
      console.log(`Good Bye, ${name}!`);
    }

    function helloAndGoodBye(name) {
      console.log(`Hello and Good Bye, ${name}!`);
    }

    function whatsUp(name) {
      console.log(`What's up?, ${name}!!!`);
    }

    let allFunctions = [hello, goodBye, helloAndGoodBye, whatsUp];
    let randomNumber = Math.floor(Math.random() * allFunctions.length);

    // call the random function and pass in our argument
    allFunctions[randomNumber]("Pixel");
  </script>
</body>

</html>

Hey Kirupa!

Your video helped a lot!
I have a question. Is it possible to pick a random item from an array but every item only once? And when every item picked once from the array returning to another item that is not in the array? Thank you!

Hey mate, could you clarify that line please, or was it a typo?

This might be what your after…
It’s a generator version of one of Kirupa’s responses above.

The function takes an array [], clones it using spread operator [...], slices a random item and yields that random item while the clone array is not empty.

If you press the get show button, the h1 will display the random show.
If you hit the reset button it restarts the generator function.

> <!DOCTYPE html>
> <html lang="en">
> <head> <meta charset="UTF-8">
> <script>
> 
> var myShows = ['Bones', 'Psych', 'Big Bang Theory', 'Mad Men', 'Breaking Bad', 'Modern Family', 'Game of Thrones', 'Dexter'];
> 
> var getRandomItem = function* (array) {
>     let clone = [...array]
>     let  getIndex =  function(arr){ return Math.floor(Math.random() * arr.length)}
> while(clone.length !== 0) yield clone.splice(getIndex(clone),1).pop();
> return 'RESET LIST'
> };
> 
> var randoms = getRandomItem(myShows);
> 
> const logRandom = () => {let val = randoms.next().value; if (val !== undefined) document.querySelector('h1').textContent = val;};
> const reset = () => {randoms = getRandomItem(myShows); document.querySelector('h1').textContent = 'CLICK FOR RANDOM SHOW';}
> 
> const load = () => {
> document.getElementById('random').addEventListener('click', logRandom);
> document.getElementById('reset').addEventListener('click', reset);
> }
> document.addEventListener('DOMContentLoaded', load)
> 
> </script>
> </head>
> <body>
> <button id = 'random'>Get Random Show</button>
> <button id = 'reset'>Reset</button>  
> <br>
> <h1>CLICK FOR RANDOM SHOW>
> </body>
> </html>

So, I am trying to create an online questionnaire.
I want to have the first two pages in order.
Then have 5 random pages.
Finally, return to have the last two pages in order.
I did randomise the pages that I wanted but now they are looping infinitely.

Thank you a lot. This does what I wanted! Now I need to apply it to my work and see if it works. Thanks again

2 Likes

You could probably throw in a couple of yield statements before the while loop and a couple after, to get the first/ last pages like this:

> var getRandomPage = function* (array) {
>     let clone = [...array]
>     let  getIndex =  function(arr){ return Math.floor(Math.random() * arr.length)}
>   yield 'page1.html';
>   yield 'page2.html';
>   while(clone.length !== 0) yield clone.splice(getIndex(clone),1).pop();
>   yield 'page8.html';
>   yield 'page9.html';
> return 'submit.html'
> };

Thank you.
Code works but somewhat. Pages do not upload. It opens like the screenshot. Any idea why?
Screenshot 2021-07-15 at 16.12.09

On second thoughts the generator might mess with a back button…

What is the code you are using to load the page? :grinning:

It would create a different order each time, but you could cache the results via localStorage after generating the pages array one time :grinning:

let randomlinks=[]

randomlinks[0]= href = “Task1A.html”

randomlinks[1]= href = “Task1B.html”

randomlinks[2]= href = “Task1C.html”

randomlinks[3]= href = “Task1D.html”

randomlinks[4]= href = “Task2A.html”

randomlinks[5]= href = “Task2B.html”

randomlinks[6]= href = “Task2C.html”

randomlinks[7]= href = “Task2D.html”

randomlinks[8]= href = “Task3A.html”

randomlinks[9]= href = “Task3B.html”

randomlinks[10]= href = “Task3C.html”

randomlinks[11]= href = “Task3D.html”

randomlinks[12]= href = “Task4A.html”

randomlinks[13]= href = “Task4B.html”

randomlinks[14]= href = “Task4C.html”

randomlinks[15]= href = “Task4D.html”

let getRandomPage = function* (array) {

let clone = […array]

let getIndex = function(arr){ return Math.floor(Math.random() * arr.length)}

while(clone.length !== 0) yield clone.splice(getIndex(clone),1).pop();

return ‘Final.html’

};

let randoms = getRandomPage(randomlinks);

const logRandom = () => {let val = randoms.next().value; if (val !== undefined) document.querySelector(‘html’).textContent = val;};

const load = () => {

document.getElementById(‘random’).addEventListener(‘click’, logRandom);

document.addEventListener(‘DOMContentLoaded’, load)

AND

<button type="submit" id="random" class="btn"><a  >Next </a></button>

Yeah you don’t need the generator :slightly_smiling_face:. I was just having a bit of fun with it. It would go well for a quiz night or bingo… :laughing:

Multi page forms can add a whole lot of complexity really fast. It might be better to keep it simple.

Maybe you could do one long <form> but break it up into <fieldsets> and use them as “pages” only diplaying the current fieldset by toggling classes and display: none, so it would look and feel like page changes.

I used a similar method for an accordion <form> a while ago and it worked a treat.

You could then start by shuffling your random array and concating with the first/ last fieldset id’s.

var firstPages = ['pageOne', 'pageTwo'];
var radoms = ['pageThree', 'pageFour', 'pageFive', 'pageSix'];
var lastPages = ['pageSeven' , 'pageEight'];

randoms.shuffle();
let formPages = firstPages.concat(randoms, lastPages);
var pos = 0;

const changePage = function (direction) { document.querySelector('.displayed').classList.remove('displayed'); 
direction === 'forward' ? pos = pos + 1 : pos = pos -1
let id = formPages[pos];
document.getElementById(id).classList.add('displayed');
}
const forwardButton = () => changePage('forward);
const backButton =() => changePage('back');

You would also need Kirupas Array.prototype.shuffle function

Well I’m off to bed :slightly_smiling_face:

Below is a full example of what I was talking about last night.

Every time you load or refresh the page it will shuffle the middle <fieldset>/ “pages” and display them in the “random in middle” order.

The advantage of this is that you don’t need to submit or collect the data for every page, do fetch requests or use cache/ local storage.

When you submit the form, it will submit the data for all the <fieldset> / “pages”. This worked pretty well for an accordion <form> I did in the past.

If this is some kind of test, you might want to add a function to flag every time the page is loaded/ reloaded.

    <!DOCTYPE html>
    <html lang="en">
    <head> <meta charset="UTF-8">
    <script>
    Array.prototype.shuffle = function () {
      let input = this;
      for (let i = input.length - 1; i >= 0; i--) {
        let randomIndex = Math.floor(Math.random() * (i + 1));
        let itemAtIndex = input[randomIndex];
        input[randomIndex] = input[i];
        input[i] = itemAtIndex;
      }
      return input;
    }
    var firstPages = ['pageOne', 'pageTwo'];
   var randoms = ['pageThree', 'pageFour', 'pageFive', 'pageSix'];
    var lastPages = ['pageSeven' , 'pageEight'];
    randoms.shuffle();

    let formPages = firstPages.concat(randoms, lastPages);
    var pos = 0;
 const changePage = function (direction) { 
document.querySelector('.displayed').classList.remove('displayed'); 
    direction === 'forward' ? pos = pos + 1 : pos = pos -1;
    let id = formPages[pos];
    document.getElementById(id).classList.add('displayed');
    }
const forwardButton = () => {if (pos !== (formPages.length - 1)) changePage('forward');return}  const backButton =() => { if(pos !== 0) changePage('back'); return}
        </script>
        <style>
        fieldset{display:none;}
        .displayed{display: inline-block;}
        </style>
        </head>
        <body>
        <button onclick="forwardButton()">Forward</button>
        <button onclick = 'backButton()'>Back</button>
        <br>
        <form>
        <fieldset id = 'pageOne' class = 'displayed'
        <h1>Page 1</h1>
        </fieldset>
        <fieldset id = 'pageTwo'class = ''>
            <h1>PAGE 2</h1>
        </fieldset>
        <fieldset id = 'pageThree' class = ''>
            <h1>PAGE 3</h1>
        </fieldset>
        <fieldset id = 'pageFour' class = ''>
            <h1>PAGE 4</h1>
        </fieldset>
        <fieldset id = 'pageFive' class = ''>
            <h1>PAGE 5</h1>
        </fieldset>
        <fieldset id = 'pageSix' class = ''>
            <h1>PAGE 6</h1>
        </fieldset>
        <fieldset id = 'pageSeven'class = '' >
            <h1>PAGE 7</h1>
        </fieldset>
        <fieldset id = 'pageEight' class = ''>
            <h1>PAGE 8</h1>
        </fieldset>
    </form>
    </body>
    </html>

Obviously this isn’t styled, a page change animation would be better and the <head> needs filling out.

There are better ways to do this however this is the simplest way I can think of for somebody who may not have the experience to go about it other ways.

I hope this helps :slightly_smiling_face:

Hi Steve,

Thank you a lot for your help. I will try and let you know if it worked!

Unfortunately, this code does not work with .html (PageOne.html). It does not work and does not load pages. But thank you for your time.

I tested it, it works. I don’t think you are understanding how it works.

The form is not actually split into multiple pages… It is a single HTML document.

It appears like there are separate pages but you are hiding and displaying parts of the same single page.

If you want to use the above code:

1- change the <fieldset id = 'pageOne' > id’s to your page names e.g. <fieldset id = 'Task1A'>

2- Change the firstPages, randoms, lastPages [] array items to your page names e.g. let firstPages = ['Task1A', 'Task1B', 'Task1C']

3- Populate the <fieldset> with your questions <label> and <input> (a fieldset is a “page”)

4- Configure your <input type= 'submit'>

5- add meta tags to the <head>

6- style the pages.

Or you could find form you like out there and see if we can help you add the shuffle pages to it… :slightly_smiling_face:

I think you are not understanding my problem.
I did all the things you wrote. I changed all the things.
BUT it does not load the pages. It works only with h1’s like you did. But I don’t want to see the names of my pages. I actually want them to load.
I am sorry if I offended you. I thanked you but your code didn’t work for me.
And yes I will continue to look and write to other forms because my problem is not solved yet.

ALSO, yes I have multiple pages. Because in each page I have a task and only 1 question.