PHP sum array values by item.qty - php

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

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;
?>

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.)

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.

Related

Subtract a number from an array in succession - decreasing the original number and array items

I need a function in PHP where I have a number I want to subtract from 3 items in an array but one after the other decreasing the original number and if it depletes one of the 3 items moves on to the next.
for example if these are my starting variable and array
$original_number = 15;
$pots = [
'pot_1' => 10,
'pot_2' => 10,
'pot_3' => 10,
];
Lets say I want to subtract 15 from all three pots items one after the other leaving a pot item at 0 (if it goes in to minus) and then moving on to the next pot until the original number is depleted.
So that would mean this would be the outcome...
$original_number = 0;
$pots = [
'pot_1' => 0,
'pot_2' => 5,
'pot_3' => 10,
];
function substract($number, $array) {
for($array as $key => $value) {
if($value > $number) {
$array[$key] = $value - $number;
$number = 0;
return $number;
} else {
$number -= $value;
$array[$key] = 0;
}
}
return $number;
}
You could then have $original_number = substract($original_number, $pots)

How do you find the closest value to a number in an associative array?

I have an associative array with 15 different companies and their stock prices, formatted as shown below:
$CloseStockPrice ('Business'=>50.5. 'Business two'=>100.5, .....)
I have found the average stock price:
$Average = (array_sum($CloseStockPrice)/count($CloseStockPrice));
The average ends up being 161.
But I now need to find the closest number (absolute terms) to that average value (161) within the associative array. I need to display the business and the stock value.
My most recent attempt:
function computeClosest(array $CloseStockPrice)
{
$closest = 161;
for ($i = 161; $i < count($CloseStockPrice) ; $i++)
{
if ($closest === 161)
{
$closest = $CloseStockPrice[$i];
} else if ($CloseStockPrice[$i] > 161 && $CloseStockPrice[$i] <= abs($closest))
{
$closest = $CloseStockPrice[$i];
} else if ($CloseStockPrice[$i] < 161 && -$CloseStockPrice[$i] < abs($closest))
{
$closest = $CloseStockPrice[$i];
return $closest;
}
}
}
Any suggestions?
While you loop through your array of business entries, cache the businesses with prices with the smallest absolute difference involving the average value.
While you might expect a single value in many cases, the fact that multiple qualifying businesses is possible means that you must keep an array of qualifying businesses for most accurate results.
A linear (single loop) process (O(n)) will outperform a sort algorithm (O(n log n)).https://stackoverflow.com/q/56506410/2943403
Code: (Demo)
$closeStockPrice = [
'A' => 50,
'B' => 155,
'C' => 75,
'D' => 245,
'E' => 300,
'F' => 100,
'G' => 153,
];
$average = array_sum($closeStockPrice) / count($closeStockPrice);
$bestDistances = [];
foreach ($closeStockPrice as $business => $price) {
$distance = abs($average - $price);
$current = current($bestDistances);
if (!$bestDistances || $current > $distance) {
$bestDistances = [$business => $distance]; // new best distance
} elseif ($current === $distance) {
$bestDistances[$business] = $distance; // push business with same distance
}
}
var_export([
'average' => $average,
'bestDistances' => $bestDistances,
'bestBusinessPrices' => array_intersect_key($closeStockPrice, $bestDistances)
]);
Output:
array (
'average' => 154,
'bestDistances' =>
array (
'B' => 1,
'G' => 1,
),
'bestBusinessPrices' =>
array (
'B' => 155,
'G' => 153,
),
)
I'm not here to agree with the stock market, but you may want to know what they're up to:
<?php
class AvgSorter{
public $sorted = [];
public function sort($array){
$s = &$this->sorted; array_splice($s, 0);
foreach($array as $k => $v){
$s[$k] = $v;
}
$avg = array_sum($s)/count($s);
uasort($s, function($a, $b) use($avg){
$a = abs($a-$avg); $b = abs($b-$avg);
if($a === $b){
return 0;
}
return $a > $b ? 1 : -1;
});
return $this;
}
public function firstKey(){
return array_keys($this->sorted)[0];
}
public function firstValue(){
return $this->sorted[$this->firstKey()];
}
public function lastKey(){
return array_keys($this->sorted)[count($this->sorted)-1];
}
public function lastValue(){
return $this->sorted[$this->lastKey()];
}
}
$array = ['business 1'=>1000.32, 'business 2'=>5.15, 'business 3'=>10.22, 'business 4'=>4.01, 'business 5'=>10.82, 'business 6'=>3.12, 'business 7'=>1.01];
$avgSorter = new AvgSorter; $firstKey = $avgSorter->sort($array)->firstKey();
$firstValue = $avgSorter->firstValue(); $lastKey = $avgSorter->lastKey();
$lastValue = $avgSorter->lastValue();
$test = 'first key:'.$firstKey.', first value:'.$firstValue.', last key:'.$lastKey.', last value:'.$lastValue;
?>

Getting sum of associated array calculation

I am working on this demo code. How can I get the sum of associated array while they multiplied by other element in the array?
I need to multiple each ['Age'] to it's associated ['Rank'] and get the sum at end. so the result should be
(40 * 2) + (36 * 4) + (26 * 2) = 204
Here is the code:
<?php
$arr = array(
["Name" => "Ali", "Age"=>40, "Rank"=>2],
["Name" => "John", "Age"=>36, "Rank"=>4],
["Name" => "Fred", "Age"=>26, "Rank"=>2],
);
$sum = array_sum(array_column($arr, 'Age'));
echo $sum;
Using "custom" code you can do it like so:
$arr = array(
["Name" => "Ali", "Age"=>40, "Rank"=>2],
["Name" => "John", "Age"=>36, "Rank"=>4],
["Name" => "Fred", "Age"=>26, "Rank"=>2],
);
$sum = 0;
foreach ($arr as &$el)
$sum += $el['Age'] * $el['Rank'];
echo $sum;
Or you probably also can do something like this:
$sum = array_reduce($arr, function($carry, $item) {
return $carry + $item['Age'] * $item['Rank'];
}, 0);
You could use array_reduce to sum a custom function (the age value multiplied by the rank value):
$sum = array_reduce($arr, function($carry, $item) {
$carry += ($item['Age'] * $item['Rank']);
return $carry;
});

How to write a function that returns a subset of elements based on the probability % stored in the element?

I have the following array
$arr = array(
"person1" => 10,
"person2" => 10,
"person3" => 20,
"person4" => 25,
"person5" => 35,
);
I would like to write a function that takes $arr as an argument and returns 3 elements of the array based on the values stored in each element.
For example if the returned subset yields
$newArr = array(
"person5" => 35,
"person1" => 10,
"person4" => 25,
);
There was a 35% chance that person5 would be the first element stored in $newArr based on the value stored in $arr['person5'] divided by the sum of values stored in the remaining elements.
$arr['person5']/($arr['person5'] + $arr['person4'] + $arr['person3'] + $arr['person2'] + $arr['person1'])
There was a ~15% chance that person1 would be the second element stored in $newArr based on the value stored in $arr['person1'] divided by the sum of values stored in the remaining elements.
$arr['person1']/($arr['person4'] + $arr['person3'] + $arr['person2'] + $arr['person1'])
There was a ~45% chance that person4 would be the second element stored in $newArr based on the value stored in $arr['person4'] divided by the sum of values stored in the remaining elements.
$arr['person4']/($arr['person4'] + $arr['person3'] + $arr['person2'])
How could I write a function that does this?
You're looking for a roulette wheel selection algorithm, see: http://en.wikipedia.org/wiki/Fitness_proportionate_selection
$count = 3;
$arr = array(
"person1" => 10,
"person2" => 10,
"person3" => 20,
"person4" => 25,
"person5" => 35,
);
$result = array();
// sort from low to high
asort($arr);
// loop 3 times (based on count)
while ($count > 0){
// get the sum of all persons
$sum = 0;
foreach ($arr as $rank){
$sum += $rank;
}
// get a random value between 0 and sum
$delta = rand(0, $sum);
$current = 0;
// keep looping over each item, increasing rank untill $sum has surpassed delta
// see each item as as person containing a portion of the slice. The bigger the value, the greater the change of being selected
$current = 0;
foreach ($arr as $name => $rank){
$current += $rank;
if ($delta <= $current){
$result[$name] = $rank;
break;
}
}
unset($arr[$name]);
$count--;
}

Get min and max value in PHP Array

I have an array like this:
array (0 =>
array (
'id' => '20110209172713',
'Date' => '2011-02-09',
'Weight' => '200',
),
1 =>
array (
'id' => '20110209172747',
'Date' => '2011-02-09',
'Weight' => '180',
),
2 =>
array (
'id' => '20110209172827',
'Date' => '2011-02-09',
'Weight' => '175',
),
3 =>
array (
'id' => '20110211204433',
'Date' => '2011-02-11',
'Weight' => '195',
),
)
I need to extract minimal and maximal Weight values.
In this example
$min_value = 175
$max_value = 200
Any help on how to do this ?
Thank you !
Option 1. First you map the array to get those numbers (and not the full details):
$numbers = array_column($array, 'weight')
Then you get the min and max:
$min = min($numbers);
$max = max($numbers);
Option 2. (Only if you don't have PHP 5.5 or better) The same as option 1, but to pluck the values, use array_map:
$numbers = array_map(function($details) {
return $details['Weight'];
}, $array);
Option 3.
Option 4. If you only need a min OR max, array_reduce() might be faster:
$min = array_reduce($array, function($min, $details) {
return min($min, $details['weight']);
}, PHP_INT_MAX);
This does more min()s, but they're very fast. The PHP_INT_MAX is to start with a high, and get lower and lower. You could do the same for $max, but you'd start at 0, or -PHP_INT_MAX.
foreach ($array as $k => $v) {
$tArray[$k] = $v['Weight'];
}
$min_value = min($tArray);
$max_value = max($tArray);
For the people using PHP 5.5+ this can be done a lot easier with array_column. Not need for those ugly array_maps anymore.
How to get a max value:
$highest_weight = max(array_column($details, 'Weight'));
How to get the min value
$lowest_weight = min(array_column($details, 'Weight'));
It is interesting to note that both the solutions above use extra storage in form of arrays (first one two of them and second one uses one array) and then you find min and max using "extra storage" array. While that may be acceptable in real programming world (who gives a two bit about "extra" storage?) it would have got you a "C" in programming 101.
The problem of finding min and max can easily be solved with just two extra memory slots
$first = intval($input[0]['Weight']);
$min = $first ;
$max = $first ;
foreach($input as $data) {
$weight = intval($data['Weight']);
if($weight <= $min ) {
$min = $weight ;
}
if($weight > $max ) {
$max = $weight ;
}
}
echo " min = $min and max = $max \n " ;
How about without using predefined functions like min or max ?
//find max
$arr = [4,5,6,1];
$val = $arr[0];
$n = count($arr);
for($i=0;$i<$n;$i++) {
if($val < $arr[$i]) {
$val = $arr[$i];
}
}
echo $val;
//find min
$arr = [4,5,6,1];
$val = $arr[0];
$n = count($arr);
for($i=0;$i<$n;$i++) {
if($val > $arr[$i]) {
$val = $arr[$i];
}
}
echo $val;
$num = array (0 => array ('id' => '20110209172713', 'Date' => '2011-02-09', 'Weight' => '200'),
1 => array ('id' => '20110209172747', 'Date' => '2011-02-09', 'Weight' => '180'),
2 => array ('id' => '20110209172827', 'Date' => '2011-02-09', 'Weight' => '175'),
3 => array ('id' => '20110211204433', 'Date' => '2011-02-11', 'Weight' => '195'));
foreach($num as $key => $val)
{
$weight[] = $val['Weight'];
}
echo max($weight);
echo min($weight);
<?php
$array = array (0 =>
array (
'id' => '20110209172713',
'Date' => '2011-02-09',
'Weight' => '200',
),
1 =>
array (
'id' => '20110209172747',
'Date' => '2011-02-09',
'Weight' => '180',
),
2 =>
array (
'id' => '20110209172827',
'Date' => '2011-02-09',
'Weight' => '175',
),
3 =>
array (
'id' => '20110211204433',
'Date' => '2011-02-11',
'Weight' => '195',
),
);
foreach ($array as $key => $value) {
$result[$key] = $value['Weight'];
}
$min = min($result);
$max = max($result);
echo " The array in Minnumum number :".$min."<br/>";
echo " The array in Maximum number :".$max."<br/>";
?>
$Location_Category_array = array(5,50,7,6,1,7,7,30,50,50,50,40,50,9,9,11,2,2,2,2,2,11,21,21,1,12,1,5);
asort($Location_Category_array);
$count=array_count_values($Location_Category_array);//Counts the values in the array, returns associatve array
print_r($count);
$maxsize = 0;
$maxvalue = 0;
foreach($count as $a=>$y){
echo "<br/>".$a."=".$y;
if($y>=$maxvalue){
$maxvalue = $y;
if($a>$maxsize){
$maxsize = $a;
}
}
}
echo "<br/>max = ".$maxsize;
print fast five maximum and minimum number from array without use of sorting array in php
:-
<?php
$array = explode(',',"78, 60, 62, 68, 71, 68, 73, 85, 66, 64, 76, 63, 81, 76, 73,
68, 72, 73, 75, 65, 74, 63, 67, 65, 64, 68, 73, 75, 79, 73");
$t=0;
$l=count($array);
foreach($array as $v)
{
$t += $v;
}
$avg= $t/$l;
echo "average Temperature is : ".$avg." ";
echo "<br>List of seven highest temperatsures :-";
$m[0]= max($array);
for($i=1; $i <7 ; $i++)
{
$m[$i]=max(array_diff($array,$m));
}
foreach ($m as $key => $value) {
echo " ".$value;
}
echo "<br> List of seven lowest temperatures : ";
$mi[0]= min($array);
for($i=1; $i <7 ; $i++)
{
$mi[$i]=min(array_diff($array,$mi));
}
foreach ($mi as $key => $value) {
echo " ".$value;
}
?>

Categories