2

I wonder if there is a faster way to sum i.e. the weight of each item by qty.

$items = [
    [
        'qty'    => 1,
        'weight' => 1,
    ],
    [
        'qty'    => 2,
        'weight' => 1,
    ],
    [
        'qty'    => 3,
        'weight' => 1,
    ],
];

$totalWeight = 0.0;
foreach ($items as $item) {
    $totalWeight += $item['weight'] * $item['qty'];
}
echo $totalWeight . PHP_EOL;

If i would not need the qty offset, i just could use

array_sum(array_column($items, 'weight'))

But this wont work ofc in this example.

Anybody has an idea if and how this could be done faster?

Thanks /cottton

EDIT

Test script:

$items = [];
for ($i = 0; $i < 1000; $i++) {
    $items[] = [
        'foo'    => 1,
        'qty'    => $i,
        'bar'    => 2,
        'weight' => $i,
        'baz'    => 3,
    ];
}
$totalWeight = 0.0;


$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
    $totalWeight = 0.0;
    foreach ($items as $item) {
        $totalWeight += $item['weight'] * $item['qty'];
    }
}
$elapsed = sprintf('%f', microtime(true) - $start);
echo "Elapsed: {$elapsed}\r\n";
echo "Total weight: {$totalWeight}\r\n";
// Elapsed: 0.744311
// Total weight: 332833500
4
  • What do you mean by "faster"? i.e what is the reason making you think that your solution is slower? Commented May 3, 2018 at 17:40
  • 1
    @SaidbakR i learned that a lot of ppl out there are awesome and have nice ideas to get the same result using a different way in less time :) Commented May 3, 2018 at 19:10
  • Good edit by adding time. Commented May 3, 2018 at 21:12
  • I wonder... where is this data coming from? What is generating it in this structure? Is a sql result set? Commented Sep 28, 2019 at 9:00

3 Answers 3

2

Use https://www.w3schools.com/php/func_array_reduce.asp

    <?php
    $items = [
        [
            'qty'    => 1,
            'weight' => 1,
        ],
        [
            'qty'    => 2,
            'weight' => 1,
        ],
        [
            'qty'    => 3,
            'weight' => 1,
        ],
    ];


    $totalWeight = array_reduce($items, 
        function($acc, $e) { return $acc + ($e['weight'] * $e['qty']); });
    echo $totalWeight . PHP_EOL;

?>
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks but unfortunately this is a bit slower than the foreach (see edit ^) - foreach 0.744311, your method 1.174606 :(
2

You can map array_product over the inner arrays and array_sum the resulting array of products.

$totalWeight = array_sum(array_map('array_product', $items));

(This may not work if the example you showed was simplified for the the question here and the inner arrays include other fields that aren't shown.)

4 Comments

Unfortunately there are more offsets in each item :)
@cottton ah, too bad. I could add another array function to filter it down to just those keys, but at that point it would end up more complex than just array_reduce as in the other answer.
or your foreach, for that matter.
This answer is completely incorrect, right?
0

Here is a method that loops the unique weights and finds the sum of qty of each weights and multiply it with the weight.

In your case it makes one loop.

$weights = array_column($items, 'weight');
$result =0;
Foreach(array_unique($weights) as $weight){
    // Finds the items with the weight 1 for example
    $weightitems = preg_grep("/". $weight . "/", $weights);
    // Sum quantity of items with the weight of 1 and multiply with weight (1).
    $result += array_sum(array_column(array_intersect_key($items, $weightitems), "qty")) * $weight;
}
Echo $result;

Notice I added a "5" just to see that it worked correct in my example code.

https://3v4l.org/V293P


A slightly more optimized code, but still not faster than a simple loop as far as I can see.
I can make it 4-ish times slower depending on the input but I can't get it closer than that.

$weights =array_unique(array_column($items, 'weight'));
$result =[];
Foreach($weights as $weight){
    $weightitems = array_filter($items, function ($var) use ($weight) {
        return ($var['weight'] == $weight);
    });
    $result[] = array_sum(array_column($weightitems, "qty")) * $weight;
}
Echo array_sum($result) ."\n";

https://3v4l.org/aB7d9


How accurate does it have to be?

This is faster than looping but is off by 1 on 3000 items and a total of 17992.
Is that within error margin, then this is faster.

I calculate the average of the sumproduct of the two columns using array_sum, array_column and count.

$count = count($items);
$weights = array_sum(array_column($items, 'weight'));
$qty = array_sum(array_column($items, 'qty'));

Echo ($qty * $weights)/$count ."\n";
// 17991 (should be 17992)

https://3v4l.org/c3CIg

I think I have tried all possible solutions now.

6 Comments

tried that and this is way to slow. It is that slow that 1 run take the time of 10000 runs of the foreach loop.
What array do you input? Is it the example in question? This code is probably more suitable on a large array with many different weights and quantities
@cottton that code creates an array of 1000 unique items, obviously this code is not suitable for all unique values. This code is supposed to group "similar" items and count them as a group. If that is a true representative of your input then you can discard this as an answer. If it's not a representative of your input then we can test it on something else more correct.
Well, it should get the actual sum. An average is not usable - at least not for me.
Sorry, i dont know what you expect as an answer. "how accurate does it have to be" - It has to be accurate. Thats it.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.