1

I have an array of Objects for a quiz like so.

[
    {
        answer: "Patience"
        question: "Name a GNR song..."
        questionId: 13
        questiontype: "text"
        userId: 1
    },
    {
        answer: "ABC"
        question: "Select three MJ songs..."
        questionId: 14
        questiontype: "checkbox"
        userId: 1
    },
    {
        answer: "Thriller"
        question: "Name three MJ songs..."
        questionId: 14
        questiontype: "checkbox"
        userId: 1
    }
]

What I am trying to do is display them on a page. So at the moment I am doing something like this

{quizData.map((item, index) => {
  return (
    <div key={index}>
      <Col xs={12}>
        <p className="text-start quiz-text">
          <strong>
            Question {item.question}
          </strong>
        </p>
      </Col>
      <Col xs={12}>
        <p className="text-start argent c-font quiz-text">{item.answer}</p>
      </Col>
    </div>
  );
})}

The problem is that this will display a new question row for each answer. So for the data above, I see something like this

Name a GNR song
    Patience

Select 3 MJ songs
    ABC
    
Select 3 MJ songs
    Thriller

What I am trying to do is have only one question, but if that question has multiple answers, display these as part of that question. So the above would be something like

Name a GNR song
    Patience

Select 3 MJ songs
    ABC
    Thriller

I presume I have to match the questionId somehow, but not sure how I can achieve this within my map?

Any advice appreciated.

Thanks

TTBox
  • 117
  • 1
  • 8

4 Answers4

2

What you are trying to do is group each object of the array by questionId. So in order to do that, you can implement a vanilla version of groupBy() as mentioned in this answer by the following way

const groupBy = (list, keyGetter) => {
    const map = new Map();
    list.forEach((item) => {
         const key = keyGetter(item);
         const collection = map.get(key);
         if (!collection) {
             map.set(key, [item]);
         } else {
             collection.push(item);
         }
    });
    return map;
}

After that you can simply use it as

// list of your question objects
var questions = [
    {
        answer: "Patience",
        question: "Name a GNR song...",
        questionId: 13,
        questiontype: "text",
        userId: 1,
    },
    {
        answer: "ABC",
        question: "Select three MJ songs...",
        questionId: 14,
        questiontype: "checkbox",
        userId: 1,
    },
    {
        answer: "Thriller",
        question: "Name three MJ songs...",
        questionId: 14,
        questiontype: "checkbox",
        userId: 1,
    }
];

// groupBy function here shall return a Map object having questionId as key and
// list of objects having that questionId as value as specified in callback passed in
const myMap = groupBy(questions, x => x.questionId)

// Now you can simply iterate over the myMap object by using forEach
myMap.forEach(
    group => { // group is the list of objects having same questionId
        console.log(group[0].question);
        group.map( // you can simply map your group like this
            object => console.log("   ", object.answer) 
        )
    }
)

// OUTPUT:

// Name a GNR song...
//     Patience
// Select three MJ songs...
//     ABC
//     Thriller

More about Map forEach

phentnil
  • 2,195
  • 2
  • 14
  • 22
1

You can use array.filter() or for loop to modify the array berfore implementation . You have to make right array before implementing in map in jsx

1

I would personally change the structure of the data to keep questions and answers apart, if you have control of that.

However if you don't you could do something like this:

{quizData.map((item, index) => {
  return (
    <div key={index}>
      <Col xs={12}>
        <p className="text-start quiz-text">
          <strong>
            Question {item.question}
          </strong>
        </p>
      </Col>
      <Col xs={12}>
        {quizData.filter(answer => answer.questionId === item.questionId).map(answer => {
          return <p>{ answer.answer }</p>
        })}
      </Col>
    </div>
  );
})}

^^ this will produce a duplicate still

Solution:

const ComponentName = () => {
  const questions = [
    {
      id: 13,
      question: "Name a GNR song..."
    }
  ]

  const answers = [
    {
      id: 1,
      questionId: 13
      answer: "Patience"
      questiontype: "text"
    }
  ]
  
  return (
    <div>
    
      {questions.map((question, index) => {
        return (
          <div key={index}>
            <Col xs={12}>
              <p className="text-start quiz-text">
                <strong>
                  Question {question.question}
                </strong>
              </p>
            </Col>
            <Col xs={12}>
              {answers.filter(answer => answer.questionId === question.id).map(answer => {
                return <p className="text-start argent c-font quiz-text">{answer.answer}</p>
              })}
            </Col>
          </div>
        );
      })}
    
    </div>
  )
  
}

If you can modify the data like i show here, this should fix your issue

Shaded
  • 158
  • 1
  • 9
  • I made a mistake in my first answer, I didn't realize you don't have an ID per question so I changed the filter to use a different property – Shaded Nov 03 '21 at 12:57
  • Thanks, that helps a lot. It puts all the answers under a question, but it is still repeating questions. So if a question has 3 answers, I see that question 3 times now with 3 answers each time. So I think I need to somehow include item.question in there somewhere? – TTBox Nov 03 '21 at 13:03
  • 1
    Actually, thinking it though, you really should change the data structure as even using my example above, you'll have duplicates. You need the questions and answers to be in two different arrays and then you can `.filter` the answer array per question to get the answers for just that question – Shaded Nov 03 '21 at 13:05
  • I made another edit to the answer for you, that should work, if you can edit the data – Shaded Nov 03 '21 at 13:14
1

You can first get unique question IDs into an array as follows using Map Constructor:

var uniqueQIDs = [...new Map(data.map(obj => [obj.questionId, obj])).values()].map(obj => obj.questionId);

Then you can loop on each question ID and filter your original data based on that as follows:

{uniqueQIDs.map((qID, index) => {
  return (
    <div key={index}>
      <Col xs={12}>
        <p className="text-start quiz-text">
          <strong>
            Question {qID}
          </strong>
        </p>
      </Col>
      <Col xs={12}>
        {quizData.filter(obj => obj.questionId === qID).map(obj => {
          return <p>{ obj.answer }</p>
        })}
      </Col>
    </div>
  );
})}
adzo261
  • 538
  • 3
  • 13