4

I have a specific usecase where I want to combine all objects from an array into one new array. So I have a form in my website where users while booking are adding participants. They can add as many participants as they want. I save them in database as JSON and now I want to form a array of all participants so that I can show them all in the table.

So I first fetch all transactions from a specific listing which I get as an Array of objects and then I loop through them all and only get the transaction.attributes.protectedData.questions which holds the participants of each transaction.

So my transactions object looks like:

[
   {
      "type":"transaction",
      "attributes":{
         "createdAt":"2021-06-24T08:50:26.911Z",
         "protectedData":{
            "questions":[
               [
                  {
                     "question":"name",
                     "answer":"Mario North"
                  },
                  {
                     "question":"email",
                     "answer":"[email protected]"
                  }
               ]
            ],
            "ticketType":{
               "name":"Main entry",
               "quantity":1
            }
         }
      }
   },
   {
      "type":"transaction",
      "attributes":{
         "createdAt":"2021-06-24T08:48:57.646Z",
         "protectedData":{
            "questions":[
               [
                  {
                     "question":"name",
                     "answer":"John Adkins"
                  },
                  {
                     "question":"email",
                     "answer":"[email protected]"
                  }
               ],
               [
                  {
                     "question":"name",
                     "answer":"Thomas Smith"
                  },
                  {
                     "question":"email",
                     "answer":"[email protected]"
                  }
               ]
            ],
            "ticketType":{
               "name":"General entry",
               "quantity":2
            }
         }
      }
   }
]

So I need to loop through each transaction, then loop through questions and each new array in questions array is a new participant. In each participant I need to save the createdAt and ticketType values from transaction property.

So my final array/object needs to look like this:

[
   {
      "createdAt":"2021-06-24T08:50:26.911Z",
      "ticketType":"Main entry",
      "name":"Mario North",
      "email":"[email protected]"
   },
   {
      "createdAt":"2021-06-24T08:48:57.646Z",
      "ticketType":"General entry",
      "name":"John Adkins",
      "email":"[email protected]"
   },
   {
      "createdAt":"2021-06-24T08:48:57.646Z",
      "ticketType":"General entry",
      "name":"Thomas Smith",
      "email":"[email protected]"
   }
]

So I can get the list of participants with createdAt and ticketType added to each of them. But I don't know how can I get my question/answer to appear as my wanted object I posted upper. This is what I have:

export const denormalisedParticipantList = transactions => {
  let participants = [];

  transactions.map(transaction => {
    const createdAt = transaction.attributes.createdAt;
    const questions = transaction.attributes.protectedData?.questions;
    const ticketType = transaction.attributes.protectedData?.ticketType?.name;

    return questions.map(q => {
      // Form new participant object
      const participant = {
        createdAt,
        ticketType,
        ...Object.fromEntries(q.map(({ question, answer }) => [question, answer])),
      };

      // Push new participant
      participants.push(participant);
    });
  });

  return participants;
};

I've been trying to figure it out since last night and I can't make it work. Can anyone please help me figure out how can I make a final array from my transactions object, I would be extremly thankful.

2
  • Show your attempt so we can see how you tried to fix it. Commented Jun 24, 2021 at 10:02
  • I will post it at the bottom of my question. Thanks! Commented Jun 24, 2021 at 10:18

3 Answers 3

2

Destructuring can be a powerful way of keeping track of what you are accessing in a complex object. Here with flatMap() to return a single flattened array and Object.fromEntries() to map the questions array to individual objects.

const input = [{ "type": "transaction", "attributes": { "createdAt": "2021-06-24T08:50:26.911Z", "protectedData": { "questions": [[{ "question": "name", "answer": "Mario North" }, { "question": "email", "answer": "[email protected]" }]], "ticketType": { "name": "Main entry", "quantity": 1 } } } }, { "type": "transaction", "attributes": { "createdAt": "2021-06-24T08:48:57.646Z", "protectedData": { "questions": [[{ "question": "name", "answer": "John Adkins" }, { "question": "email", "answer": "[email protected]" }], [{ "question": "name", "answer": "Thomas Smith" }, { "question": "email", "answer": "[email protected]" }]], "ticketType": { "name": "General entry", "quantity": 2 } } } }]

const result = input.flatMap((
  {
    attributes: {
      createdAt,
      protectedData: {
        questions,
        ticketType: { name: ticketType } }
    }
  }
) => (
  questions.map(p => ({
    createdAt,
    ticketType,
    ...Object.fromEntries(p.map(({ question, answer }) => [question, answer]))
  }))
));

console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

4 Comments

Hey pilchard, your solution works perfectly and it is really clean. Thank you so much. Can you explain me what is the difference using flatMap instead of using it the way I posted it in my question? I just edited it. Should I use the flatMap too?
You are using map() in your code, but not utilizing the returned array. This is inefficient, but is the reason that you don’t end up with a nested array since you are manually pushing each nested object to the result array. You should replace your map() calls with forEach() or utilize the return from the map.
Thanks for the comments pilchard. I have marked your answer as accepted.
No worries, glad it helped.
1
var array = []
data.forEach((element) => {
var object = { created: null,ticketType: null,name: null,email: null }
object.created = element.attributes.createdAt;
object.ticketType = element.attributes.protectedData.ticketType.name;
var tmp_data = element.attributes.protectedData.questions;
tmp_data.forEach((tmp) => {
    tmp.forEach((item) => {
        if(item.question == "name"){
            object.name = item.answer;
        }else{
            object.email = item.answer;
        }
    })
    array.push(object);
})
})

try this :)

1 Comment

Thanks a lot Michel, your answer helped aswell! Looks really clean. Is it fine if I use this javascript type of answer in my React project?
1

You can simply use reduce and map.

const data = [{ "type": "transaction", "attributes": { "createdAt": "2021-06-24T08:50:26.911Z", "protectedData": { "questions": [[{ "question": "name", "answer": "Mario North" }, { "question": "email", "answer": "[email protected]" }]], "ticketType": { "name": "Main entry", "quantity": 1 } } } }, { "type": "transaction", "attributes": { "createdAt": "2021-06-24T08:48:57.646Z", "protectedData": { "questions": [[{ "question": "name", "answer": "John Adkins" }, { "question": "email", "answer": "[email protected]" }], [{ "question": "name", "answer": "Thomas Smith" }, { "question": "email", "answer": "[email protected]" },{ "question": "gender", "answer": "male" }]], "ticketType": { "name": "General entry", "quantity": 2 } } } }]


const output = data.reduce(
  (a, b) => [
    ...a,
    ...b.attributes.protectedData.questions.map(e => ({
      createdAt: b.attributes.createdAt,
      ticketType: b.attributes.protectedData.ticketType.name,
      ...e.reduce((acc, cur) => ({ ...acc, [cur.question]: cur.answer }), {}),
    })),
  ],
  []
);

console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }

2 Comments

Thanks a lot, this looks really clean. Tho what should I do if I have more questions (as sometimes name and email won't be the only questions). Is there a way to dynamically make them?
@MiodragSumaric, yes you can. I updated my answer. You can see the last item contains a gender question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.