Home > Software engineering >  Merge Object array based on id
Merge Object array based on id

Time:07-24

Hi I am calling an API where it returns an Object containing questions and answers like so

{
   "questions":[
      {
         "id":1,
         "questionHeader":"Some question header",
         "questionText":"Some question text?",
      },
      {
         "id":2,
         "questionHeader":"Some question header",
         "questionText":"Some question text?",
      },
   ],
   "answers":[
      {
         "id":1,
         "questionId":1,
         "answer":"Some answer",
      },
      {
         "id":2,
         "questionId":1,
         "answer":"Some answer",
      },
      {
         "id":3,
         "questionId":2,
         "answer":"Some answer",
      },
   ]
}

What I am trying to achieve is to get the answers embedded within their respective question based on a questions id and an answers questionId. So for the above, I am trying to achieve something like the following.

{
   "questions":[
      {
         "id":1,
         "questionHeader":"Some question header",
         "questionText":"Some question text?",
         "answers":[
          {
             "id":1,
             "questionId":1,
             "answer":"Some answer",
          },
          {
             "id":2,
             "questionId":1,
             "answer":"Some answer",
          },
        ]
      },
      {
         "id":2,
         "questionHeader":"Some question header",
         "questionText":"Some question text?",
         "answers":[
          {
            "id":3,
            "questionId":2,
            "answer":"Some answer",
          },
        ]
      },
   ],
}

I have something which adds some answers but not as an array of objects per question. Currently I have this

const newItem = data.questions.map((t1) => ({
    ...t1,
    ...data.answers.find((t2) => t2.questionId === t1.id),
}));
console.log(newItem);

I have set up a JSFiddle. How can I achieve my desired output?

Thanks

CodePudding user response:

Index the questions by ID first, then iterate over the answers. The question ID will make it easy to access the associated question - just use bracket notation to look it up on the indexed object, and you can push to that question's answers. After iteration is finished, turn the questions back into an array again.

const data={questions:[{id:1,questionHeader:"Some question header",questionText:"Some question text?"},{id:2,questionHeader:"Some question header",questionText:"Some question text?"}],answers:[{id:1,questionId:1,answer:"Some answer"},{id:2,questionId:1,answer:"Some answer"},{id:3,questionId:2,answer:"Some answer"}]};

const questionsById = Object.fromEntries(
  data.questions.map(question => [question.id, { ...question, answers: [] }])
);
for (const answer of data.answers) {
  questionsById[answer.questionId].answers.push(answer);
}
const output = Object.values(questionsById);
console.log(output);

The above is O(n) because there are no nested loops. If you wanted to tweak your nested loop approach (O(n ^ 2)) so that it works. .filter instead of .find so you can get an array of answers, and prefix the property in the object there with answers: instead of spreading.

const data={questions:[{id:1,questionHeader:"Some question header",questionText:"Some question text?"},{id:2,questionHeader:"Some question header",questionText:"Some question text?"}],answers:[{id:1,questionId:1,answer:"Some answer"},{id:2,questionId:1,answer:"Some answer"},{id:3,questionId:2,answer:"Some answer"}]};

const newItem = data.questions.map((t1) => ({
    ...t1,
    answers: data.answers.filter((t2) => t2.questionId === t1.id),
}));
console.log(newItem);

CodePudding user response:

Array#filter can be used to find all the answers with a specific question id. (Note that Array#find only returns the first element matching a condition.)

const data={questions:[{id:1,questionHeader:"Some question header",questionText:"Some question text?"},{id:2,questionHeader:"Some question header",questionText:"Some question text?"},],answers:[{id:1,questionId:1,answer:"Some answer"},{id:2,questionId:1,answer:"Some answer"},{id:3,questionId:2,answer:"Some answer"},]};
const newItem = data.questions.map((t1) => ({
    ...t1,
    answers: data.answers.filter((t2) => t2.questionId === t1.id),
}));
console.log(newItem);

CodePudding user response:

This is my take... simple forEach

var response = {
  "questions": [{
      "id": 1,
      "questionHeader": "Some question header",
      "questionText": "Some question text?",
    },
    {
      "id": 2,
      "questionHeader": "Some question header",
      "questionText": "Some question text?",
    },
  ],
  "answers": [{
      "id": 1,
      "questionId": 1,
      "answer": "Some answer",
    },
    {
      "id": 2,
      "questionId": 1,
      "answer": "Some answer",
    },
    {
      "id": 3,
      "questionId": 2,
      "answer": "Some answer",
    },
  ]
}

response.answers.forEach(function(answer) {
  var question = response.questions.find(function(item) {
    return (item.id == answer.questionId)
  })
  question.answers = (question.answers || [])
  question.answers.push(answer)
  
})
delete response.answers;
console.log(response)

  • Related