0

I have the following objects

Person {
   name: string
   birthday: Date
   lifeEvents: LifeEvent[]
}

LifeEvent {
   eventId: number
   message: string
   comments: string
}

As the data comes in chunks, I will have an array of Person with one object that has name and birthday with values but lifeEvents is empty (Think of this one like a parent object.)

All other objects won't have birthday and name populated and will have only one LifeEvent with either eventId and message with values or eventId and comments

Within that array, I need to get the parent object, which has name and birthday with values, then get all lifeEvents from the remaining objects, merge all items that contains same eventId into a LifeEvent then push it to lifeEvents of the parent.

I have tried array.reduce, array.map but can't figure out a way of combining those objects into one.

My output should be only one Person with all lifeEvents merged by eventId

Sample data:

let results = [
{ 
   name: 'Test1',
   birthday: '2022-06-14',
   lifeEvents: null
},
{ 
   name: null,
   birthday: null,
   lifeEvents: [
        {
           eventId: 1,
           message: 'First event',
           comments: null
        }
   ]
},
{ 
   name: null,
   birthday: null,
   lifeEvents: [
        {
           eventId: 2,
           message: 'Second event',
           comments: null
        }
   ]
},
{ 
   name: null
   birthday: null
   lifeEvents: [
        {
           eventId: 1,
           message: null,
           comments: 'First event comment'
        }
   ]
},
{ 
   name: null
   birthday: null
   lifeEvents: [
        {
           eventId: 2,
           message: null,
           comments: 'Second event comment'
        }
   ]
},
]

Appreciate any help.

4
  • 1
    could you plz put some sample data for testing? Commented Jun 14, 2022 at 19:40
  • Do you get the data from a database? This is usually an indication that you should make some changes in how you query your data. Wrong grouping is often easiest to fix by fixing the query. It's more performant that way too. Commented Jun 14, 2022 at 19:40
  • No, it's actually a combination of text files that are being parsed from uploaded data. Commented Jun 14, 2022 at 19:49
  • Please post what you have tried so far. Commented Jun 14, 2022 at 19:50

2 Answers 2

1

Premise: The data structure you are using is wrong, you should try to use arrays with homogenous models.

That said I used a reduce method, with a condition to treat the first element in a different way from the other ones.

Last thing, you said merge lifeEvents, I assume you meant to overwrite the nullish values for events with same ids, if you want to overwrite all values then you can omit the merge utility function I wrote.

let results = [{
    name: 'Test1',
    birthday: '2022-06-14',
    lifeEvents: null,
  },
  {
    name: null,
    birthday: null,
    lifeEvents: [{
      eventId: 1,
      message: 'First event',
      comments: null,
    }, ],
  },
  {
    name: null,
    birthday: null,
    lifeEvents: [{
      eventId: 2,
      message: 'Second event',
      comments: null,
    }, ],
  },
  {
    name: null,
    birthday: null,
    lifeEvents: [{
      eventId: 1,
      message: null,
      comments: 'First event comment',
    }, ],
  },
  {
    name: null,
    birthday: null,
    lifeEvents: [{
      eventId: 2,
      message: null,
      comments: 'Second event comment',
    }, ],
  },
];

const merge = (o1, o2) => {
  const r = {...o1}
  Object.keys(o1).forEach(k => r[k] = o2[k] || o1[k])
  return r
}

const r = results.reduce((o, curr, i) => {
  if (i === 0) {
    return { ...o,
      lifeEvents: []
    };
  } else {
    const currentEvent = curr.lifeEvents[0]
    const idx = o.lifeEvents.findIndex((_o) => _o.eventId === currentEvent.eventId);
    if (idx !== -1) return { ...o,
      lifeEvents: o.lifeEvents.map((_o, i) => i === idx ? merge(_o, currentEvent) : _o)
    }
    else return { ...o,
      lifeEvents: [...o.lifeEvents, currentEvent]
    }
  }
}, results[0]);

console.log("RESULT:", r);

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

2 Comments

Thanks @Cesare. I was able to resolve the issue with your snippet and it also gave me a better understanding on how to chain actions.
@Wagner D'Amaral glad to help!
1

The following produces the requested result based on examples provided:

let results = [
  { 
     name: 'Test1',
     birthday: '2022-06-14',
     lifeEvents: null
  },
  { 
     name: null,
     birthday: null,
     lifeEvents: [
          {
             eventId: 1,
             message: 'First event',
             comments: null
          }
     ]
  },
  { 
     name: null,
     birthday: null,
     lifeEvents: [
          {
             eventId: 2,
             message: 'Second event',
             comments: null
          }
     ]
  },
  { 
     name: null,
     birthday: null,
     lifeEvents: [
          {
             eventId: 1,
             message: null,
             comments: 'First event comment'
          }
     ]
  },
  { 
     name: null,
     birthday: null,
     lifeEvents: [
          {
             eventId: 2,
             message: null,
             comments: 'Second event comment'
          }
     ]
  },
];

// extract parent
const parentResult = results.find((result) => result.name);

// generate unique events from sibling entities
const uniqueEvents = new Map();
results.forEach((result) => result.lifeEvents?.forEach(
  (lifeEvent) => {
    if (uniqueEvents.has(lifeEvent.eventId)) {
      updateEvent(lifeEvent);
    } else {
      uniqueEvents.set(lifeEvent.eventId, { eventId: lifeEvent.eventId, message: lifeEvent.message, comments: lifeEvent.comments});
    }
  })
);

// function to update event that is already stored in uniqueEvents
function updateEvent(lifeEvent) {
  const existingLifeEvent = uniqueEvents.get(lifeEvent.eventId);
  if (lifeEvent.message) existingLifeEvent.message = lifeEvent.message;
  if (lifeEvent.comments) {
    if (existingLifeEvent.comments) {
      existingLifeEvent.comments.concat(lifeEvent.comments)
    } else {
      existingLifeEvent.comments = lifeEvent.comments;
    }
  }
}

// populate lifeEvents inside the parentResult
parentResult.lifeEvents = [];
uniqueEvents.forEach((uniqueId) => {
  parentResult.lifeEvents.push(uniqueId);
});

console.log(parentResult);

Comments

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.