Sam Brueton
Sam Brueton

Reputation: 69

Group and Sum data in ReactJS

I have the following data objects in a list within in my React project:

{projectId: "11111", startTime: "Mon May 11 2020 19:19:07", duration: "15"}
{projectId: "22222", startTime: "Mon May 11 2020 19:24:07", duration: "45"}
{projectId: "11111", startTime: "Mon May 11 2020 19:45:07", duration: "15"}
{projectId: "11111", startTime: "Tue May 12 2020 11:00:07", duration: "30"}
{projectId: "22222", startTime: "Tue May 12 2020 12:19:07", duration: "15"}

I want to be able to group and sum this data by Date and ProjectId so i would expect the following output:

{projectId: "11111", Date: "Mon May 11 2020", TotalDuration: "30"}
{projectId: "22222", Date: "Mon May 11 2020", TotalDuration: "45"}
{projectId: "11111", Date: "Tue May 12 2020", TotalDuration: "30"}
{projectId: "22222", Date: "Tue May 12 2020", TotalDuration: "15"}

I am new to ReactJS and can not seem to find a solution to do this.

Upvotes: 0

Views: 1114

Answers (4)

Andriy
Andriy

Reputation: 15442

you can try to group your data by productId + date without the time part, using Array.reduce():

const data = [
  {projectId: "11111", startTime: "Mon May 11 2020 19:19:07", duration: "15"},
  {projectId: "22222", startTime: "Mon May 11 2020 19:24:07", duration: "45"},
  {projectId: "11111", startTime: "Mon May 11 2020 19:45:07", duration: "15"},
  {projectId: "11111", startTime: "Tue May 12 2020 11:00:07", duration: "30"},
  {projectId: "22222", startTime: "Tue May 12 2020 12:19:07", duration: "15"}
];

const result = data.reduce((res, curr) => {
  // remove time
  const date = curr.startTime.substr(0, curr.startTime.length - 9);
  // build the key (productId + date without the time part)
  const key = `${curr.projectId}_${date}`;
  res[key] = res[key] || {
    projectId: curr.projectId,
    Date: date,
    TotalDuration: "0"
  };
  // sum durations as integers and convert the sum back to string
  res[key].TotalDuration = (+res[key].TotalDuration + +curr.duration) + '';
  return res;
}, {});

// print only values
console.log(Object.values(result));

BTW: the solution is in plain JS (nothing React specific)

Upvotes: 1

julianobrasil
julianobrasil

Reputation: 9357

You can do this:

const groupedDataObj = {};

for(let entry of oridinalData) {
  const regexDate = /^([A-Za-z]){3} ([A-Za-z]){3} \d{2} \d{4}/;
  const key = entry.projectId + '||' + entry.startTime.split(regexDate)[0]
  if(!groupedDataObj.hasOwnProperty(key)) {
    groupedDataObj[key] = { totalDuration: 0 };
  }
  groupedDataObj[key].totalDuration += +entry.TotalDuration;
}

const groupedData = Object.keys(groupedDataObj).map(key => {
  const splittedKey = key.split('||');
  const productId = splittedKey[0];
  const Date = splittedKey[1];
  return {
    productId,
    Date,
    TotalDuration: groupedDataObj[key].totalDuration
  };
});

Upvotes: 0

Patrick Roberts
Patrick Roberts

Reputation: 51816

You can do this, though I'd recommend using an ISO-format datetime string instead of this format for startTime, since the Date constructor is not guaranteed to be able to parse this format in every implementation.

const data = [
  {projectId: "11111", startTime: "Mon May 11 2020 19:19:07", duration: "15"},
  {projectId: "22222", startTime: "Mon May 11 2020 19:24:07", duration: "45"},
  {projectId: "11111", startTime: "Mon May 11 2020 19:45:07", duration: "15"},
  {projectId: "11111", startTime: "Tue May 12 2020 11:00:07", duration: "30"},
  {projectId: "22222", startTime: "Tue May 12 2020 12:19:07", duration: "15"}
];

const updateOrCreate = (map, key, update, create) => map.set(
  key,
  update(map.has(key) ? map.get(key) : create())
);

const sums = [
  ...data.map(
    o => ({ ...o, Date: new Date(o.startTime).toDateString() })
  ).reduce(
    (outer, o) => updateOrCreate(
      outer,
      o.Date,
      inner => updateOrCreate(
        inner,
        o.projectId,
        sum => +o.duration + sum,
        () => 0
      ),
      () => new Map()
    ),
    new Map()
  )
].flatMap(
  ([Date, projects]) => [...projects].map(
    ([projectId, duration]) => ({
      projectId, Date, TotalDuration: `${duration}`
    })
  )
);

console.log(sums);

Upvotes: 1

Denis Stukalov
Denis Stukalov

Reputation: 1242

You can use reduce and filter for group your items:

const items = [{projectId: "11111", startTime: "Mon May 11 2020 19:19:07", duration: "15"},
{projectId: "22222", startTime: "Mon May 11 2020 19:24:07", duration: "45"},
{projectId: "11111", startTime: "Mon May 11 2020 19:45:07", duration: "15"},
{projectId: "11111", startTime: "Tue May 12 2020 11:00:07", duration: "30"},
{projectId: "22222", startTime: "Tue May 12 2020 12:19:07", duration: "15"}]

const trimTime = (dateTime) => dateTime.split(' ').splice(0, 4).join(' ')

const groupItems = items.reduce((acc,rec) => {
  const itemsInAcc = acc.filter(a=>a.projectId === rec.projectId && a.startTime === trimTime(rec.startTime))
  if (itemsInAcc.length > 0) {
    itemsInAcc[0].duration = (+itemsInAcc[0].duration) + (+rec.duration)
  }
  else {
    acc = [...acc, {...rec, startTime: trimTime(rec.startTime)}]
  }
  return acc
}, [])

Upvotes: 1

Related Questions