SaraRandolph
SaraRandolph

Reputation: 53

How to combine like objects within array of objects and insert new object?

I have an array of objects for a podcast app that looks like this:

[{ id: "uuid-1"
   timeInSeconds: 1000
   dateListened: "2021-01-01T15:57:17.000Z" }, // <---same day
{  id: "uuid-2"
   timeInSeconds: 4900
   dateListened: "2021-01-01T16:57:17.000Z" }, // <---same day 
{  id: "uuid-3"
   timeInSeconds: 3200
   dateListened: "2021-09-01T16:57:17.000Z" }, 
{  id: "uuid-4"
   timeInSeconds: 6000
   dateListened: "2021-10-01T16:57:17.000Z" } ]

I want to create a function that combines activity times if the dateListened is on the same day. I want it to look something like this:

[{ id: "uuid-new" 
   timeInSeconds: 5900 // <---- combined seconds listened on Jan 1
   dateListened: "2021-01-01T15:57:17.000Z" },
{  id: "uuid-3"
   timeInSeconds: 3200
   dateListened: "2021-09-01T16:57:17.000Z" }, 
{  id: "uuid-4"
   timeInSeconds: 6000
   dateListened: "2021-10-01T16:57:17.000Z" } ]

My closest attempts have been to use a .map() with a nested .some() but I'm still far off enough that it's not even worth posting my tries. Does anyone have any hints or ideas of what direction to try next? Thank you!

Upvotes: 0

Views: 43

Answers (2)

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48630

You could reduce the items and map them to a date key, retrieve the values, and then map them to single objects.

You can key on the following:

let date = new Date(info.dateListened).toLocaleDateString('en-US')

const data = [
  { id: "uuid-1",
    timeInSeconds: 1000,
    dateListened: "2021-01-01T15:57:17.000Z" }, // <---same day
  { id: "uuid-2",
    timeInSeconds: 4900,
    dateListened: "2021-01-01T16:57:17.000Z" }, // <---same day 
  { id: "uuid-3",
    timeInSeconds: 3200,
    dateListened: "2021-09-01T16:57:17.000Z" }, 
  { id: "uuid-4",
    timeInSeconds: 6000,
    dateListened: "2021-10-01T16:57:17.000Z" }
 ];

const result = Object
  .values(data.reduce((acc, info) =>
    (date =>
      ({ ...acc, [date]: [...(acc[date] || []), info ] }))
    (new Date(info.dateListened).toLocaleDateString('en-US')), {}))
  .map(value => value.length === 1 ? value : {
    id: 'uuid-new',
    timeInSeconds: value.reduce((sum, { timeInSeconds }) => sum + timeInSeconds, 0),
    dateListened: value[0].dateListened
  });

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

Upvotes: 1

DecPK
DecPK

Reputation: 25408

You can achieve the result using reduce and only add the timeInSeconds if its year, month, and date is exactly the same/equal.

const arr = [
  {
    id: "uuid-1",
    timeInSeconds: 1000,
    dateListened: "2021-01-01T15:57:17.000Z",
  },
  {
    id: "uuid-2",
    timeInSeconds: 4900,
    dateListened: "2021-01-01T16:57:17.000Z",
  },
  {
    id: "uuid-3",
    timeInSeconds: 3200,
    dateListened: "2021-09-01T16:57:17.000Z",
  },
  {
    id: "uuid-4",
    timeInSeconds: 6000,
    dateListened: "2021-10-01T16:57:17.000Z",
  },
];

const result = arr.reduce((acc, curr) => {
  const { id, timeInSeconds, dateListened } = curr;
  const searchSameDateElement = acc.find((o) => {
    const first = new Date(o.dateListened);
    const second = new Date(dateListened);
    const isYearSame = first.getFullYear() === second.getFullYear();
    const isMonthSame = first.getMonth() === second.getMonth();
    const isDateSame = first.getDate() === second.getDate();
    return isYearSame && isMonthSame && isDateSame;
  });
  if (!searchSameDateElement) {
    acc.push(curr);
  } else {
    searchSameDateElement.timeInSeconds += timeInSeconds;
  }
  return acc;
}, []);

console.log(result);

Upvotes: 0

Related Questions