PandaMastr
PandaMastr

Reputation: 695

Sum array of objects value based on month - ReactJS

So first of all, because I am using a ChartJS I need to generate an array of months. The creation is looking like this :

const [generatedMonths, setGeneratedMonths] = useState<string[]>([])
const [totalValues, setTotalValues] = useState<number[]>([])

const months = []
const dateStart = moment()
const dateEnd = moment().subtract(11, 'month')

while (dateEnd.isBefore(dateStart, 'day')) {
    months.push(dateEnd.format('MMM'))
    dateEnd.add(1, 'month')
}
months.push(dateEnd.format('MMM'))

setGeneratedMonths(months)

After that i receive my data like this :

  const myArray = [
    {date: "2022-04-13", vat_percentages: {21: 92.31}, total: 92.31}
    {date: "2022-05-04", vat_percentages: {21: 21.24}, total: 21.24}
    {date: "2022-05-05", vat_percentages: {21: 47.41}, total: 47.41}
    ]

Then in my useEffect hook i am performing the sum :

useEffect(() => {
    if (myArray.length > 0) {
        const labelValues: number[] = generatedMonths.map(
            (label, index) => {
                const result = myArray.reduce((acc, curr) => {
                    const foundItem = acc.find(
                        (item) => item.date.format('MMM') === label
                    )

                    if (foundItem) {
                        foundItem.total += curr.total
                    } else {
                        acc.push(curr)
                    }

                    return acc
                }, [])
            }
        )

      setTotalValues(labelValues)
    }


},[myArray])

So the final idea would be to have a sum for each month based on my find method based on the total value. Like this:

console.log(totalValues)
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92.31, 68.65]

Upvotes: 0

Views: 758

Answers (1)

Mushroomator
Mushroomator

Reputation: 9168

You can massively simplify the solution using the fact that values for month are always going to be between 1-12 (inclusive). Therefore you can use an array of length 12 and use month - 1 as an index to update the sum for a given month.

const myArray = [{
  date: "2022-04-13",
  vat_percentages: {
    21: 92.31
  },
  total: 92.31
}, {
  date: "2022-05-04",
  vat_percentages: {
    21: 21.24
  },
  total: 21.24
}, {
  date: "2022-05-05",
  vat_percentages: {
    21: 47.41
  },
  total: 47.41
}]

const labelValues = myArray.reduce((acc, curr) => {
  // parse a month to number e.g. "04" => 4 then substract 1 to get the index within the array 4 - 1 => 3
  const idx = Number(curr.date.split("-")[1]) - 1;
  acc[idx] += curr.total;
  return acc
}, new Array(12).fill(0))

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

The output should be interpreted as follows:

0 (Jan) 1 (Feb) 2 (Mar) 3 (Apr) 4 (May) 5 (Jun) 6 (Jul) 7 (Aug) 8 (Sep) 9 (Oct) 10 (Nov) 11 (De)
0 0 0 92.31 68.64999999999999 0 0 0 0 0 0 0

Edit

To reflect your requirement of having to set the values at certain positions according to generatedMonths you could use simple modular arithmetic.

A position for a month is therefore obtained by (month + shiftValue) % 12. To determine the shift value you just need to look at the first value in generatedMonths and lookup which month of the year that is. The rest works exactly like before:

const myArray = [{
  date: "2022-04-13",
  vat_percentages: {
    21: 92.31
  },
  total: 92.31
}, {
  date: "2022-05-04",
  vat_percentages: {
    21: 21.24
  },
  total: 21.24
}, {
  date: "2022-05-05",
  vat_percentages: {
    21: 47.41
  },
  total: 47.41
}]

// mapping of month to shift value
const mapping = {
  Jan: 0,
  Feb: 11,
  Mar: 10,
  Apr: 9,
  May: 8,
  Jun: 7,
  Jul: 6,
  Aug: 5,
  Sep: 4,
  Oct: 3,
  Nov: 2,
  Dec: 1
};
// compute a shift value
const generatedMonths = ["Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May"]
const shiftBy = mapping[generatedMonths[0]];
const labelValues = myArray.reduce((acc, curr) => {
  // parse a month to number e.g. "04" => 4 then substract 1 to get the index within the array 4 - 1 => 3
  const idx = (Number(curr.date.split("-")[1]) + shiftBy - 1) % 12;
  acc[idx] += curr.total;
  return acc
}, new Array(12).fill(0))

console.log(labelValues)

Upvotes: 1

Related Questions