Search code examples
javascriptreactjsfiltermap-function

How to Return Total of the month based on dates in JavaScript(React)


I wrote a hook to calculate sales based on month-to-set data for the chart.

It works fine, but I'm not satisfied.

is there a different or easy way to avoid repeating and make code clean and extendable?

note: I'm not working with companies or teams - (self-learning)

I want to calculate the total per day in the month and return a new array of objects

Function:



const month = (date) => new Date(date).toLocaleString('default', { month: 'long' });

export const useGetChartData = (orders) => {

  const orderTotal = orders.length
  const orderTotalsArray = orders.map(order => (+order.total) + (+order.total_tax));
  const sumOrders = orderTotalsArray?.reduce((inc, a) => inc + a, 0);
  const getMonthFromDateAndTotals = orders?.map(order => (
    {
      date: (order.date_paid ? month(order.date_paid) : month(order.date_modified)),
      total: +order.total + +order.total_tax
    }));

  const sendToChart = getMonthFromDateAndTotals.filter(o => o.total > 0);
  //filter!
  const January = sendToChart.filter((d) => d.date == 'January')
  const February = sendToChart.filter((d) => d.date == 'February')
  const March = sendToChart.filter((d) => d.date == 'March')
  const April = sendToChart.filter((d) => d.date == 'April')
  const May = sendToChart.filter((d) => d.date == 'May')
  const June = sendToChart.filter((d) => d.date == 'June')
  const July = sendToChart.filter((d) => d.date == 'July')
  const August = sendToChart.filter((d) => d.date == 'August')
  const September = sendToChart.filter((d) => d.date == 'September')
  const October = sendToChart.filter((d) => d.date == 'October')
  const November = sendToChart.filter((d) => d.date == 'November')
  const December = sendToChart.filter((d) => d.date == 'December')
  // 
  const totalsPerMonths = [
    //reduce! 
    { month: 'January', total: January?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'February', total: February?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'March', total: March?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'April', total: April?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'May', total: May?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'June', total: June?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'July', total: July?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'August', total: August?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'September', total: September?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'October', total: October?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'November', total: November?.reduce((inc, a) => inc + a.total, 0) },
    { month: 'December', total: December?.reduce((inc, a) => inc + a.total, 0) },
    // 
  ];

  const dataVisualizationToChart = totalsPerMonths.filter(vd => vd.total > 0);
  return {
    dataVisualizationToChart,
    orderTotal,
    sumOrders
  };
};

usage:

  const { data: orders, isLoading: ordersLoading, errors: orderErrors} = useGetShopData("orders");
  const { dataVisualizationToChart, sumOrders } = useGetChartData(orders)

Return :

{
  "dataVisualizationToChart": [
    {"month": "February","total": 1875.96},
    { "month": "March", "total": 1362.46},
    { "month": "April","total": 66.05000000000004},
  ],
  "orderTotal": 70,
  "sumOrders": 13064
}```




Solution

  • I would suggest something like this:

    Create the monthNames object: Mapping numerical values of months to string names.

    const monthNames = {
    '01': 'January',
    '02': 'February',
    '03': 'March',
    '04': 'April',
    '05': 'May',
    '06': 'June',
    '07': 'July',
    '08': 'August',
    '09': 'September',
    '10': 'October',
    '11': 'November',
    '12': 'December'
    };
    

    Instead of creating individual arrays for each month and then calculating the totals, I used reduce to group totals by month in a single pass.

    The result of totalsPerMonths will be an object where the keys are unique dates (months) and the values are the total sums for each month.

    const totalsPerMonths = sendToChart.reduce((acc, current) => {
        if (!acc[current.date]) {
            acc[current.date] = 0;
        }
        acc[current.date] += current.total;
        return acc;
    }, {});
    

    Here, I extracted the numeric value of the month into a string name using the monthNames mapping.

    After reducing, I constructed the dataVisualizationToChart by iterating over the keys of the grouped and reduced object.

    const dataVisualizationToChart = Object.keys(totalsPerMonths).map(dateStr => {
        const [year, month] = dateStr.split('-');
        return {
            month: monthNames[month],
            total: totalsPerMonths[dateStr]
        };
    });
    

    I hope my solution helps you!

    I'd suggest you work on problems related to arrays and result aggregation on LeetCode. It really helps with solving similar tasks in the future!