Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

chrisgrim's avatar

Need help with complicated calculation

Hi, I am getting stuck with a complicated calculation. I have a material list array with multiple materials. Each material has a pivot with a percentage and a list of prices by month. The calculation I need to do is to first multiply each price in each material list by the percentage. So for the first material price1 would be 3.9 and price3 would be 5.1. Then I need to add up all the march 1st prices to get a total and so on. So the first value would be 3.9 (13*.3) + 2 (10 * .2) = 5.9 and so on.

material list:
    material1 
        pivots:
            percentage: 30
        prices:
            price1
                13.00
                march 1st
            price2
                13.00
                april 1st
            price3
                17.00
                may 1st
            price4
                11.00
                june 1st
            price5
                19.00
                july 1st
    material2:
        pivots:
            percentages: 20
        prices:
            price1
                10.00
                march 1st
            price2
                10.00
                april 1st
            price3
                13.00
                may 1st
            price4
                11.00
                june 1st
            price5
                10.00
                july 1st

I have tried to do

        computed: {
            totals() {
                return this.materialList.forEach(mat => mat.prices.map(price => price.price * how do I get the material.pivot.percentage into here? ));
            }
        },

but I am having trouble getting values where I need them.

0 likes
1 reply
piljac1's avatar
piljac1
Best Answer
Level 28

My solution may need some altering based on your date type (if it's not a string, the key should be converted to a string in the reduce part the logic to work). Also, I assumed that your date key name is "date". But basically, this is my solution (and here's a sandbox if you want to toy with it):

return Object.values(
  this.materialList
    .map((mat) =>
      mat.prices.map((price) => ({
        price: price.price * mat.pivots.percentages / 100,
        date: price.date,
      }))
    )
    .flat()
    .reduce((accumulator, price) => {
      const key = price.date;

      if (!accumulator[key]) {
        accumulator[key] = price;
      } else {
        accumulator[key].price += price.price;
      }

      return accumulator;
    }, {})
);

You might be like "woah there!", so here's what's going on:

  • First of all, in the map portion, you iterate over each material and then map each prices into basically the same price structure, but with the price that has been multiplied by the percentages. At this point, this is what you get:
[
    [
        {
            "price": 3.9,
            "date": "2021-03-01"
        },
        {
            "price": 3.9,
            "date": "2021-04-01"
        },
        {
            "price": 5.1,
            "date": "2021-05-01"
        },
        {
            "price": 3.3,
            "date": "2021-06-01"
        },
        {
            "price": 5.7,
            "date": "2021-07-01"
        }
    ],
    [
        {
            "price": 2,
            "date": "2021-03-01"
        },
        {
            "price": 2,
            "date": "2021-04-01"
        },
        {
            "price": 2.6,
            "date": "2021-05-01"
        },
        {
            "price": 2.2,
            "date": "2021-06-01"
        },
        {
            "price": 2,
            "date": "2021-07-01"
        }
    ]
]
  • Then, you can see that you have an unnecessary level of arrays, so then you call .flat() on that result, which gives you:
[
    {
        "price": 3.9,
        "date": "2021-03-01"
    },
    {
        "price": 3.9,
        "date": "2021-04-01"
    },
    {
        "price": 5.1,
        "date": "2021-05-01"
    },
    {
        "price": 3.3,
        "date": "2021-06-01"
    },
    {
        "price": 5.7,
        "date": "2021-07-01"
    },
    {
        "price": 2,
        "date": "2021-03-01"
    },
    {
        "price": 2,
        "date": "2021-04-01"
    },
    {
        "price": 2.6,
        "date": "2021-05-01"
    },
    {
        "price": 2.2,
        "date": "2021-06-01"
    },
    {
        "price": 2,
        "date": "2021-07-01"
    }
]
  • With all prices at the same level, you can now use reduce to build an object keyed by the price date. If no key matches the date, create a new property with the date as the key and the entire price object as the value. Else, if there is actually a key matching the date, simply increment its price by the currently iterated price object's price. That gives us the following object:
{
    "2021-03-01": {
        "price": 5.9,
        "date": "2021-03-01"
    },
    "2021-04-01": {
        "price": 5.9,
        "date": "2021-04-01"
    },
    "2021-05-01": {
        "price": 7.699999999999999,
        "date": "2021-05-01"
    },
    "2021-06-01": {
        "price": 5.5,
        "date": "2021-06-01"
    },
    "2021-07-01": {
        "price": 7.7,
        "date": "2021-07-01"
    }
}
  • Finally, you probably want the prices as an array for easier manipulation, so that's what Object.values does. And there it is, the final result:
[
    {
        "price": 5.9,
        "date": "2021-03-01"
    },
    {
        "price": 5.9,
        "date": "2021-04-01"
    },
    {
        "price": 7.699999999999999,
        "date": "2021-05-01"
    },
    {
        "price": 5.5,
        "date": "2021-06-01"
    },
    {
        "price": 7.7,
        "date": "2021-07-01"
    }
]

P.S. Of course you should probably round prices afterwards because floats are not 100% precise, so they can cause the problem you see on May 1st.

Please or to participate in this conversation.