Search code examples
javascriptarraysjavascript-objectsshuffle

Shuffle Quiz API answers with JavaScript


I'm pulling 10 random questions from an API but currently, the correct answer is set to always be 'D'. I'm struggling to find a way to randomize the answers so that the correct answer is not always 'D' or any other option I set in the code. The JSON is an array of objects but because incorrect_answers is an inner array within the object separate from correct_answer, this is where I'm getting confused. If I shuffle the inner array, that just shuffles options A, B and C. D remains a separate property outside of that that inner array.

Hope someone can help. Thanks in advance. see app screenshot here

This is a sample from the JSON

{
  "response_code": 0,
  "results": [
    {
      "category": "Entertainment: Video Games",
      "type": "multiple",
      "difficulty": "easy",
      "question": "What is the most expensive weapon in Counter-Strike: Global Offensive?",
      "correct_answer": "Scar-20/G3SG1",
      "incorrect_answers": [
      "M4A1",
      "AWP",
      "R8 Revolver"
      ]
    },
    {
      "category": "Entertainment: Cartoon & Animations",
      "type": "multiple",
      "difficulty": "easy",
      "question": "Who is the only voice actor to have a speaking part in all of the Disney Pixar feature films? ",
      "correct_answer": "John Ratzenberger",
      "incorrect_answers": [
      "Tom Hanks",
      "Dave Foley",
      "Geoffrey Rush"
      ]
    }
}

This is my code so far

const main = () => {
       let score = 0;
       let questionCounter = 0;
       let availableQuestions = [];
       let currentQuestion = {};

// target DOM elements
     const question = document.querySelector(".question-container");

     const choices = document.querySelectorAll(".choice-txt");

     const selection = document.querySelectorAll(".choice-container");

// API endpoint     
     const endPoint = `https://opentdb.com/api.php?amount=10&difficulty=easy&type=multiple`;

// fetch data from the API
     fetch(endPoint)
     .then((res)=>{
            return res.json();
     })
     .then((json)=>{
            question.innerHTML = `<p>
            ${json.results[0].question}
            </p>`

            // populate the 4 answer options
            // currently the correct answer is always D
            choices[0].innerText = `${json.results[0].incorrect_answers[0]}`
            choices[1].innerText = `${json.results[0].incorrect_answers[1]}`
            choices[2].innerText = `${json.results[0].incorrect_answers[2]}`
            choices[3].innerText = `${json.results[0].correct_answer}`
     })
     .catch((err)=>{
            console.log(`An error occured ${err}`);
     })



     selection[0].addEventListener("click",()=>{
       console.log(`Answer "A" selected`)
     })
     selection[1].addEventListener("click",()=>{
       console.log(`Answer "B" selected`)
     })
     selection[2].addEventListener("click",()=>{
       console.log(`Answer "C" selected`)
     })
     selection[3].addEventListener("click",()=>{
       console.log(`Answer "D" selected`)
     })
}
main();


Solution

  • For this, you need to put together correct/incorrect answers into an object array. (Making normalization)

    I used the Fisher-Yates shuffle. Take a look at here


    Shuffling Part:

    function shuffle(array) {
      let currentIndex = array.length, randomIndex;
    
      // While there remain elements to shuffle.
      while (currentIndex != 0) {
    
        // Pick a remaining element.
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex--;
    
        // And swap it with the current element.
        [array[currentIndex], array[randomIndex]] = [
          array[randomIndex], array[currentIndex]];
      }
    
      return array;
    }
    

    Also, I made improvements to the code for readability.

    Normalization Part:

    // fetch data from the API
    fetch(endPoint)
      .then((res) => {
        return res.json();
      }).then((json) => {
        question.innerHTML = 
        `<p>
          ${json.results[0].question}
        </p>`
        let answers = json.results[0].incorrect_answers.map(f => {
          return {
            isCorrect: false,
            text: f
          }
        });
    
        answers.push({
          isCorrect: true,
          text: json.results[0].correct_answer
        });
    
        // populate the shuffled 4 answer options
        shuffle(answers).forEach((answer, index) => {
          choices[index].innerText = `${answer.text}`
        })
      })
      .catch((err) => {
        console.log(`An error occured ${err}`);
      })
    

    Test Part:

    function shuffle(array) {
      let currentIndex = array.length, randomIndex;
    
      // While there remain elements to shuffle.
      while (currentIndex != 0) {
    
    // Pick a remaining element.
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex--;
    
    // And swap it with the current element.
    [array[currentIndex], array[randomIndex]] = [
      array[randomIndex], array[currentIndex]];
      }
    
      return array;
    }
    
    // Create sample data.
    const results = [
      {
    "category": "Entertainment: Video Games",
    "type": "multiple",
    "difficulty": "easy",
    "question": "What is the most expensive weapon in Counter-Strike: Global Offensive?",
    "correct_answer": "Scar-20/G3SG1",
    "incorrect_answers": [
      "M4A1",
      "AWP",
      "R8 Revolver"
    ]
      },
      {
    "category": "Entertainment: Cartoon & Animations",
    "type": "multiple",
    "difficulty": "easy",
    "question": "Who is the only voice actor to have a speaking part in all of the Disney Pixar feature films? ",
    "correct_answer": "John Ratzenberger",
    "incorrect_answers": [
      "Tom Hanks",
      "Dave Foley",
      "Geoffrey Rush"
    ]
      }
    ];
    
    // Putting wrong answers
    let answers = results[0].incorrect_answers.map(f => {
      return {
    isCorrect: false,
    text: f
      }
    });
    // Also putting correct answer
    answers.push({
      isCorrect: true,
      text: results[0].correct_answer
    });
    
    //Logging shuffled answers
    shuffle(answers).forEach((answer, index) => {
      console.log(answer);
    })