Home > Software engineering >  Modify a Json file from API to suit my need
Modify a Json file from API to suit my need

Time:09-19

I am developing this quiz app using React.js which I fetch the questions from an API. Here is the first question from the JSON file I extracted from the API:

export default [{
"response_code": 0,
"results": [
    {

        "category": "Science: Computers",
        "type": "multiple",
        "difficulty": "medium",
        "question": "Which of these is the name for the failed key escrow device introduced by the National Security Agency in 1993?",
        "correct_answer": "Clipper Chip", //I want to copy this to the "incorrect_answers" array
        "incorrect_answers": [
            "Enigma Machine",
            "Skipjack",
            "Nautilus"
            //Clipper Chip
        ]
        // So I'll be able to have 4 buttons with options to choose from,
        // while having the "correct_answer" switch position but still retain its value
    },
    {

        "category": "Science: Computers",
        "type": "multiple",
        "difficulty": "medium",
        "question": "In the server hosting industry IaaS stands for...", ...

Here is what I want to do, I want to move or copy the "correct_answer" into the "incorrect_answers" array so I'll be able to have 4 options on each question when I render the JSX. Here is my code to the above:

data.incorrect_answers.push(data.correct_answer)
const answersElement = data.incorrect_answers.map((answer) => {
    console.log(answer)
    return (
        <>
            <button>
                {answer}
            </button>

        </>
    )
})

The problem with this "PUSH" method is that it duplicates the "correct_answer" buttons and doubles on each click, which is weird to me.

Remember, this data comes from an API so I can't modify it manually to suit me. I also don't want all the correct answer options to be in the same position as all the questions, I want to be able to swap their position.

Note: I have 5 questions with 4 options on each fetch call.

CodePudding user response:

I'm assuming that:

  • the JSX code is the return element or render method
  • clicking the button actually does something that is not shown here (and probably change something in the Component state

The issue is that when you click (and it does something), it will re-render your component thus running the array.push once more each time. You need to "fix" your array outside of a method that runs on re-rendering. i.e. useEffect or wherever you actually get the API response.

CodePudding user response:

const availableAnswers = [...data.incorrect_answers, data.correct_answer];
const answersElement = data.availableAnswers.map((answer) => {
    console.log(answer)
    return (
        <>
            <button>
                {answer}
            </button>

        </>
    )
})

Probably fixes your problem. Pushing on your response modifies it. And by the looks of it you are not fetching it again, but caching it somewhere.

CodePudding user response:

Since the other answers address how to merge the correct/incorrect answers I'll use my answer to address how to add the correct answer randomly to the answers array - otherwise the correct answer will always fall in the same place in the array - and also to show how verify if the clicked answer is the correct one.

// Returns an integer between 0 and 3
function rnd() {
  return Math.floor(Math.random() * 4);
}

// Make objects from each of the array elements that
// contain the name, and a boolean value to indicate whether
// its correct or not. `splice` the correct answer randomly into
//  the array, and return it
function merge(correct, incorrect) {
  const correctObj = { name: correct, correct: true };
  const answersArr = incorrect.map(answer => {
    return { name: answer, correct: false };
  });
  answersArr.splice(rnd(), 0, correctObj);
  return answersArr;
}

function Question({ question }) {

  // Destructure the answers from the question
  const {
    correct_answer: correct,
    incorrect_answers: incorrect
  } = question;

  // Use the merge function
  const answers = merge(correct, incorrect);

  if (!answers.length) return 'No answers';

  // When you click a button find the name corresponding
  // to the correct answer, and if it matches the name in
  // the button's name data attribute log "correct" otherwise
  // "incorrect"
  function handleClick(e) {
    const { name } = answers.find(answer => answer.correct);
    if (name === e.target.dataset.name) {
      console.log('Correct');
    } else {
      console.log('Incorrect');
    }
  }

  return (
    <div>
      <p>{question.question}</p>
      {answers.map(answer => {
        return (
          <button
            type="button"
            data-name={answer.name}
            className="answer"
            onClick={handleClick}
          >{answer.name}
          </button>
        );
      })}
    </div>
  );

}

const question={category:"Science: Computers",type:"multiple",difficulty:"medium",question:"Which of these is the name for the failed key escrow device introduced by the National Security Agency in 1993?",correct_answer:"Clipper Chip",incorrect_answers:["Enigma Machine","Skipjack","Nautilus"]};

ReactDOM.render(
  <Question question={question} />,
  document.getElementById('react')
);
.answer { padding: 0.25em 0.4em; margin: 0.25em; border-radius: 5px; }
.answer:hover { cursor: pointer; background-color: lightgreen; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

  • Related