2
\$\begingroup\$

I built a method using reduce of JS to aggregate together data from a DB in a better shape.

What I'm wondering is, if that method can be simplified with a more modern approach or better written. Like using a different method instead of a reduce.

The method takes the dbData which includes data in the following form:

const dbData = [{
    studyId: 'X',
    siteId: 'A',
    day: '2000-01-01',
    status: 'PENDING_CALLCENTER',
    current: 5,
    total: 17,
  },
  {
    studyId: 'X',
    siteId: 'A',
    day: '2000-01-01',
    status: 'PENDING_SITE',
    current: 3,
    total: 9,
  },
  {
    studyId: 'Y',
    siteId: 'B',
    day: '2000-01-01',
    status: 'PENDING_SITE',
    current: 3,
    total: 9,
  },
  {
    studyId: 'Y',
    siteId: 'B',
    day: '2000-01-01',
    status: 'PENDING_CALLCENTER',
    current: 3,
    total: 9,
  },
]

The data contains two same fields/values as studyId and siteId and what I aggregate is the status, current, total to be included under the same Study and Site.

The output of the above passing via the method:

[
  {
    "studyId": "X",
    "siteId": "A",
    "currents": {
      "PENDING_CALLCENTER": 5,
      "PENDING_SITE": 3
    },
    "totals": {
      "PENDING_CALLCENTER": 17,
      "PENDING_SITE": 9
    }
  },
  {
    "studyId": "Y",
    "siteId": "B",
    "currents": {
      "PENDING_SITE": 6,
      "PENDING_CALLCENTER": 3
    },
    "totals": {
      "PENDING_SITE": 18,
      "PENDING_CALLCENTER": 9
    }
  }
] 

So it is reduced to a different form where we have one object per studyId and siteId which includes the currents and totals of the status.

The method

dbData.reduce((acc, row) => {
const {
  studyId,
  siteId,
  status,
  current,
  total
} = row;
const idx = acc.findIndex(x => studyId === x.studyId && siteId === x.siteId);
const item = idx === -1 ? {
  studyId,
  siteId,
  currents: {},
  totals: {}
} : { ...acc[idx]
};
item.currents[status] = item.currents[status] ? item.currents[status] + current : current;
item.totals[status] = item.totals[status] ? item.totals[status] + total : total;
if (idx === -1) {
  acc.push(item);
} else {
  acc[idx] = item;
}
return acc;
}, []);

A working example

const dbData = [{
    studyId: 'X',
    siteId: 'A',
    day: '2000-01-01',
    status: 'PENDING_CALLCENTER',
    current: 5,
    total: 17,
  },
  {
    studyId: 'X',
    siteId: 'A',
    day: '2000-01-01',
    status: 'PENDING_SITE',
    current: 3,
    total: 9,
  },
  {
    studyId: 'Y',
    siteId: 'B',
    day: '2000-01-01',
    status: 'PENDING_SITE',
    current: 3,
    total: 9,
  },
  {
    studyId: 'Y',
    siteId: 'B',
    day: '2000-01-01',
    status: 'PENDING_CALLCENTER',
    current: 3,
    total: 9,
  },
];


const reduced = dbData.reduce((acc, row) => {
  const {
    studyId,
    siteId,
    status,
    current,
    total
  } = row;
  const idx = acc.findIndex(x => studyId === x.studyId && siteId === x.siteId);
  const item = idx === -1 ? {
    studyId,
    siteId,
    currents: {},
    totals: {}
  } : { ...acc[idx]
  };
  item.currents[status] = item.currents[status] ? item.currents[status] + current : current;
  item.totals[status] = item.totals[status] ? item.totals[status] + total : total;
  if (idx === -1) {
    acc.push(item);
  } else {
    acc[idx] = item;
  }
  return acc;
}, []);

console.log(reduced);

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

A couple of things that can be simplified/refactored:

  • Conditionals can be reduced to improve code readability.
  • No need to clone an object if it already exists. Instead, we can just update the currents and totals properties.

const dbData = [{
    studyId: 'X',
    siteId: 'A',
    day: '2000-01-01',
    status: 'PENDING_CALLCENTER',
    current: 5,
    total: 17,
  },
  {
    studyId: 'X',
    siteId: 'A',
    day: '2000-01-01',
    status: 'PENDING_SITE',
    current: 3,
    total: 9,
  },
  {
    studyId: 'Y',
    siteId: 'B',
    day: '2000-01-01',
    status: 'PENDING_SITE',
    current: 3,
    total: 9,
  },
  {
    studyId: 'Y',
    siteId: 'B',
    day: '2000-01-01',
    status: 'PENDING_CALLCENTER',
    current: 3,
    total: 9,
  },
];


const reduced = dbData.reduce((acc, row) => {
  const {
    studyId,
    siteId,
    status,
    current,
    total
  } = row;
  
  const idx = acc.findIndex(x => studyId === x.studyId && siteId === x.siteId);
  
  if (idx === -1) {
    const item = {
      studyId,
      siteId,
      currents: {
        [status]: current, 
      },
      totals: {
        [status]: total,
      },
    };
    
    acc.push(item);
  } else {
    acc[idx].currents[status] = (acc[idx].currents[status] || 0) + current;
    acc[idx].totals[status] = (acc[idx].totals[status] || 0) + total;
  }
  
  return acc;
}, []);

console.log(reduced);

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.