Help me clean up my functional JS

This is working for me, but I feel like there is a more concise way to use the filter on an array of objects. This code goes through an object containing multiple-choice questions. Each question has an array of answer objects. It looks for the answer containing 'correct === true ’ then returns the index of that answer.

I’m also getting this warning on compile, but I am returning something inside the filter function, so I am not using it entirely correctly: "Expected to return a value at the end of arrow function "

  createAnswerKey () {
    const answerKey = quizQuestions.map((question, index)=> {
      let correctAnswer =  question.answers.filter((answer) => {
        if (answer.correct === true ) {
          return ({thing: parseInt(answer.id)})
        }
      })
      return correctAnswer[0].id
    })
  }

here is a concise filter statement in the same application. I would like the above code to be more concise if possible…partly for my learning objectives to get better with functional coding and ES6 features.

   let choice = quizQuestions[this.state.counter].answers.filter(
       option => option.id === parseInt(this.state.selectedAnswers[this.state.counter])
    )

Here is the data structure (one question shown)

var quizQuestions = [
  {
    question: "A 64-year-old man presents with gradual onset of swelling in his face, a cough, a headache, shortness of breath, and, that morning, blue lips. When asked, he says that he smokes “not quite two” packs of cigarettes per day. Which test will most likely reveal the diagnosis?",
    answers: [
      {
        id: 1,
        content: "Brain MRI with diffusion-weighted imaging",
        correct: false,
        discussion: "An SVC compression from a tumor can cause cerebral sinus congestion and cerebral edema that can be identified using MRI, but CT of the chest to evaluate the vena cava is a more reasonable approach."
      },
      {
        id: 2,
        content: "Chest CT with contrast",
        correct: true,
        discussion: "Superior vena cava (SVC) syndrome is caused by occlusion of the SVC by a thrombus or mediastinal mass, and it should be easily identified with a CT scan of the chest with contrast. The symptoms usually develop over weeks to months, as a tumor grows and compresses venous return from the head and neck. The tumor is most commonly a non–small-cell cancer of the lung but can also be small-cell lung cancer or lymphoma. The syndrome can develop more quickly in the case of a thrombus, which is the most common nonmalignant cause of the condition. Other nonmalignant causes include restrictive pericarditis, mediastinal fibrosis, and goiter. Patients with SVC syndrome present with gradual onset of periorbital edema and facial swelling that is most prominent in the early morning after spending a night lying flat. This can progress to plethora of the face, edema of the upper extremities and neck, and headaches from cerebral venous backflow. Patients typically have a cough, either from the lack of venous drainage or primarily from irritation of the tumor itself. Some develop dyspnea and hypoxia, leading to cyanosis."
      },
      {
        id: 3,
        content: "Lower-extremity Doppler ultrasonography",
        correct: false,
        discussion: "PE should always be considered for patients with dyspnea and chest discomfort; identifying DVT in the leg is sufficient to make the diagnosis by inference. This patient, however, has a more gradual onset of symptoms, and facial swelling is not a recognized symptom of PE."
      },
      {
        id: 4,
        content: "Lumbar puncture and CSF cell count",
        correct: false,
        discussion: "Lumbar puncture is indicated to rule out meningitis or subarachnoid hemorrhage, but facial swelling and a cough are not usually symptoms of these conditions. Idiopathic intracranial hypertension is diagnosed and relieved with a lumbar puncture, but imaging should be done first to ensure that the patient is not at risk of herniation."
      }
    ],
    source: "Peer",
    references: [
      "Marx JA, Hockberger RS, Walls RM, eds. Rosen’s Emergency Medicine: Concepts and Clinical Practice. 8th ed. St. Louis, MO: Elsevier; 2014:129-134.",
      "Tintinalli JE, Stapczynski JS, Ma OJ, et al, eds. Tintinalli’s Emergency Medicine: A Comprehensive Study Guide. 8th ed. New York, NY: McGraw-Hill; 2012:1500-1504."
    ]
  },

That’s not how filter works: it needs to return true or false. You don’t return a value, what you do is return a new array containing only the elements that return true for the condition you give it.

Also, you aren’t trying to get the index, you’re trying to get the ID, which is not the same thing (it is an array of answers, index 0 is id 1, index 1 is id 2 and so on). So that createAnswerKey function is mapping over the quiz questions to give you an array of the ids of the correct answers.

quizQuestions.map((quizQuestion) => {
  return quizQuestion.answers.find((answer) => answer.correct === true)[id];
})

So map over the questions. For each question object, find the answer where the correct field is true, and grab its ID. This assumes there will always be one and only one correct answer for each question.

To make it more concise, use destructuring + the fact correct is a boolean:

quizQuestions.map(({answers}) => answers.find(({correct}) => correct)[id]);

To find the index instead of the ID:

quizQuestions.map(({answers}) => answers.findIndex(({correct}) => correct));

Note that this is one possible solution, there are others (for example using reduce, or using an imperative loop inside the function).

1 Like

Thanks, that helps a lot. You know, i’ve been doing this (as a hobby) for 5 years now, I hope I eventually can sort these out. A lot of my applications involve complex objects like this one.

I tried something similar to your first answer, but I got back all of the questions. I think I deleted that non-working solution so I can’t compare to see what I did wrong.

Also, the answer choices are shuffled each time the quiz is run, so ID and index are not always the same thing.

I appreciate your help & time!

UPDATE: So I need to use find instead of filter since there is only one correct answer for each question. Brilliant! I had the right idea, wrong implementation.

This is working for me, however I can’t seem to grab just the ID with this syntax, I get an error ‘id is not defined’

return quizQuestion.answers.find((answer) => answer.correct === true)[id];

I can return the whole item, and deal with it outside this mapping routine, but would like to just grab the ID.

I believe it should be .id instead of [id], since id is a property of each object.

2 Likes

That’s it! Thanks I was going crazy.