php sum of array - php

I have the following array with multiple levels. I wish to get the sum total of [price], [adults] and [childern] but have not been able traverse the levels.
The answer I should get with this example is price=380 adults=5 and children=1
Array (
[8] => Array (
[2] => Array (
[num_rooms] => 2
[adults] => Array (
[0] => 1
[1] => 1
)
[children] => Array (
[0] => 0
[1] => 0
)
[prices] => Array (
[0] => 50
[1] => 50
)
[price] => 130
[supp] => 30
)
[3] => Array (
[num_rooms] => 1
[adults] => Array (
[0] => 1
)
[prices] => Array (
[0] => 100
)
[price] => 150
[supp] => 50
)
)
[1] => Array (
[2] => Array (
[num_rooms] => 2
[adults] => Array (
[0] => 1
[1] => 1
)
[children] => Array (
[0] => 1
[1] => 0
)
[prices] => Array (
[0] => 75
[1] => 75
)
[price] => 170
[supp] => 20
)
)
)
Thanks

Two loops and a helper array:
$sums = array ( 'price' => 0, 'adults' => 0, 'children' => 0 );
foreach($array as $outer) {
foreach($outer as $inner) {
$sums['price'] += $inner['price'];
$sums['adults'] += array_sum($inner['adults']);
$sums['children'] += array_sum($inner['children']);
}
}
print_r($sums);
With a more dynamic version of the inner loop:
foreach($array as $outer) {
foreach($outer as $inner) {
foreach($sums as $key => &$v)
$v += is_array($inner[$key])
? array_sum($inner[$key])
: $inner[$key];
}
}

This should work:
$price = 0;
$adults = 0;
$children = 0;
foreach($arr as $l1_key => $l1_value) // iterates over the first level array
{
foreach($l1_value as $l2_key => $l2_value) // iterates over second level arrays
{
$price += $l2_value['price']; // add up price totals
foreach($l2_value['adults'] as $value) // iterate through adults array values
{
$adults += $value; // sum up adult count
}
foreach($l2_value['children'] as $value) // iterate through children array values
{
$children += $value; // sum up children count
}
}
}
// now $price, $adults, and $children contain the totals for each

I didn't test this code but at the same time I don't know how you got 380.. I'm seeing 350?
$sums = getSum($arr);
print_r($sums);
function getSum($arr) {
$sums = array();
$sums2 = array();
$sums['adults'] = 0;
$sums2['adults'] = 0;
$sums['children'] = 0;
$sums2['children'] = 0;
$sums['prices'] = 0;
$sums2['prices'] = 0;
foreach ($arr as $key => $value) {
$do_not_recurse = false;
switch ($key) {
case 'adults':
$do_not_recurse = true;
foreach ($value as $adults)
$sums['adults'] += $adults;
break;
case 'children':
$do_not_recurse = true;
foreach ($value as $children)
$sums['children'] += $children;
break;
case 'prices':
$do_not_recurse = true;
foreach ($value as $price)
$sums['prices'] += $price;
break;
default:
break;
}
if (is_array($value))
$sums2 = getSum($value);
}
$sums['adults'] += $sums2['adults'];
$sums['children'] += $sums2['children'];
$sums['prices'] += $sums2['prices'];
return $sums;
}

Handles any depth or array structure and just picks out the terms with the names you are looking for:
function find($term, $array) {
$count = 0;
foreach ($array as $item)
if (is_array($item)) $count += find($term, $item);
if (isset($array[$term]) {
if (is_array($array[$term])) $count += array_sum($array[$term]);
else $count += $array[$term];
}
return $count;
}
echo count('price', <the array>);
echo count('adults', <the array>);
echo count('children', <the array>);

Related

php remove empty 'columns' in multidimensional, associative array

Goal: Generate an array that includes only those 'columns' with data, even though a 'header' may exist.
Example Data:
Array (
[HeaderRow] => Array (
[0] => Employee [1] => LaborHours [2] => 0.1 [3] => 0.25 [4] => 0.5 [5] => 0.8
)
[r0] => Array (
[0] => Joe [1] => 5 [2] => [3] => [4] => 50 [5] =>
)
[r1] => Array (
[0] => Fred [1] => 5 [2] => 10 [3] => [4] => [5] =>
)
)
Desired Output:
Array (
[HeaderRow] => Array (
[0] => Employee [1] => LaborHours [2] => 0.1 [4] => 0.5
)
[r0] => Array (
[0] => Joe [1] => 5 [2] => [4] => 50
)
[r1] => Array (
[0] => Fred [1] => 5 [2] => 10 [4] =>
)
)
So, in this very dumbed down example, the HeaderRow will always have data, but if both c0 and c1 are empty (as is the case for [3] and [5]) then I want to remove. I tried iterating through with for loops like I would in other languages, but that apparently doesn't work with associative arrays. I then tried doing a transpose followed by two foreach loops, but that failed me as well. Here's a sample of my for loop attempt:
Attempt with For Loop
for ($j = 0; $j <= count(reset($array))-1; $j++) {
$empty = true;
for ($i = 1; $i <= count($array)-1; $i++) {
if(!empty($array[$i][$j])) {
$empty = false;
break;
}
}
if ($empty === true)
{
for ($i = 0; $i <= count($array); $i++) {
unset($array[$i][$j]);
}
}
}
return $array;
Attempt with transpose:
$array = transpose($array);
foreach ($array as $row)
{
$empty = true;
foreach ($row as $value)
{
if (!empty($value))
{
$empty = false;
}
}
if ($empty) {
unset($array[$row]);
}
}
$array = transpose($array);
return $array;
function transpose($arr) {
$out = array();
foreach ($arr as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$out[$subkey][$key] = $subvalue;
}
}
return $out;
}
I know the transpose one isn't terribly fleshed out, but I wanted to demonstrate the attempt.
Thanks for any insight.
We can make this more simpler. Just get all column values using array_column.
Use array_filter with a custom callback to remove all empty string values.
If after filtering, size of array is 0, then that key needs to be unset from all
subarrays.
Note: The arrow syntax in the callback is introduced since PHP 7.4.
Snippet:
<?php
$data = array (
'HeaderRow' => Array (
'0' => 'Employee','1' => 'LaborHours', '2' => 0.1, '3' => 0.25, '4' => 0.5, '5' => 0.8
),
'r0' => Array (
'0' => 'Joe', '1' => 5, '2' => '','3' => '', '4' => 50, '5' => ''
),
'r1' => Array (
'0' => 'Fred', '1' => 5,'2' => 10, '3' => '', '4' => '', '5' => ''
)
);
$cleanup_keys = [];
foreach(array_keys($data['HeaderRow']) as $column_key){
$column_values = array_column($data, $column_key);
array_shift($column_values); // removing header row value
$column_values = array_filter($column_values,fn($val) => strlen($val) != 0);
if(count($column_values) == 0) $cleanup_keys[] = $column_key;
}
foreach($data as &$row){
foreach($cleanup_keys as $ck){
unset($row[ $ck ]);
}
}
print_r($data);
It figures, I work on this for a day and have a moment of clarity right after posting. The answer was that I wasn't leveraging the Keys.:
function array_cleanup($array)
{
$array = transpose($array);
foreach ($array as $key => $value)
{
$empty = true;
foreach ($value as $subkey => $subvalue)
{
if ($subkey != "HeaderRow") {
if (!empty($subvalue))
{
$empty = false;
}
}
}
if ($empty) {
unset($array[$key]);
}
}
$array = transpose($array);
return $array;
}

Bin allocation not assigning right amounts

I have an array that can contain anything from 4 to 4000 entries, I have to evenly assign the amounts across 4 people. I am struggling just to get my code to do as I need.
As you can see my allocation to the arrays are not as desired.
Array ( [0] => Array ( [0] => 80 [7] => 40 [8] => 50 )
[1] => Array ( [1] => 80 )
[2] => Array ( [2] => 70 [6] => 30 )
[3] => Array ( [3] => 60 [5] => 40 )
)
array 0 has total of 150
array 1 has total of 80
array 2 has total of 100
array 3 has total of 100
Code:
<?php
arrayBuilder();
function arrayBuilder()
{
$list_of_items = [80, 20, 30, 40, 50, 60, 70, 80];
rsort($list_of_items);
$number_of_cont = 4;
$weight_of_items = array_sum($list_of_items);
$weight_per_cont = ($weight_of_items/$number_of_cont);
$containers = [];
$sumArray = $newArray = [];
$containersSorted = buildArray($list_of_items,$number_of_cont,$containers,$weight_per_cont);
$itemsNotAdded = itemsNotINArray($list_of_items, $containersSorted);
foreach ($containersSorted as $k => $subArray) {
foreach ($subArray as $id => $value) {
if(isset($sumArray[$k])) {
$sumArray[$k] += $value;
}else{
$sumArray[$k] = $value;
}
}
}
sort($sumArray);
$itemsNotAdded = array_values($itemsNotAdded);
foreach($itemsNotAdded as $key => $value){
foreach($sumArray as $minkey => $minval){
if($minkey == $key) {
$containersSorted[$key][] = $value;
unset($itemsNotAdded[$key]);
}
}
}
print_r($containersSorted);
};
function itemsNotINArray($list_of_items, $containersSorted){
foreach($containersSorted as $conid => $conval){
foreach($list_of_items as $key => $value){
if(key_exists($key,$conval)){
unset($list_of_items[$key]);
continue;
}
}
}
return $list_of_items;
};
function getMisMatchKey($itemsNotAdded, $containersSorted){
}
function buildArray($list_of_items,$number_of_cont,$containers,
$weight_per_cont){
foreach($list_of_items as $key => $item) {
for ($i = 0; $i < $number_of_cont; $i++) {
$total = (isset($containers[$i])) ? array_sum($containers[$i]) : 0;
if (($total + $item) < $weight_per_cont) {
$containers[$i][$key] = $item;
break;
}
}
}
return $containers;
};
?>
Should have worked it out evenly
Array ( [0] => Array ( [0] => 80 [7] => 20 )
[1] => Array ( [1] => 80 [6] => 30 )
[2] => Array ( [2] => 70 [8] => 40 )
[3] => Array ( [3] => 60 [5] => 50 )
)
This splits the array into arrays with pairs of the value 80:
public function action_binAllocation()
{
$list_of_items = [80, 20, 30, 40, 50, 60, 70, 80, 10, 70];
$sum = 80;
$evenlySortedArray = $this->findPairsInArrayWhereSum($sum, $list_of_items);
print_r($evenlySortedArray);
}
public function findPairsInArrayWhereSum($sum, $arr = [])
{
$s = [];
$evenlyDistributedArrays = [];
for ($i = 0; $i < count($arr); $i++) {
$temp = $sum - $arr[$i];
$s[] = $arr[$i];
if (in_array($temp, $s)) {
$evenlyDistributedArray[] = [$arr[$i], $temp];
}
}
return $evenlyDistributedArray;
}

Recursive count elements of multidimensional array [duplicate]

This question already has answers here:
Php recursive array counting
(7 answers)
Closed 4 months ago.
I try to make recursive function to count elements on array "levels". But can't do that for two hours already. Check example array:
Array (
[0] => Array (
[0] => Array (
[0] => Array ( )
[1] => Array ( )
)
[1] => Array ( )
)
[1] => Array (
[0] => Array (
[0] => Array (
[0] => Array ( )
[1] => Array ( )
)
)
)
)
The resulting array that count elements on different levels will be:
Array ([0] => 2, [1] => 3, [2] => 3, [3] => 2)
I made function for count total array elements, but no idea how to count each "level"
function countTotalArr($arr, $lvl) {
if ($lvl != 0) $cnt = 1;
else $cnt = 0; // don't count zero level
for ($i = 0; $i < count($arr); $i++)
$cnt += countArr($arr[$i], $lvl + 1);
return $cnt;
}
$total = countTotalArr($referralsCount, 0);
Another solution using while:
// $array is your array at the beginning of iteration
$depthMap = [];
$currentDepth = 0;
while(true) {
$depthMap[$currentDepth] = count($array);
$carry = [];
foreach($array as $item) {
if(is_array($item)) {
$carry = array_merge($carry, $item);
}
}
if(count($carry) < 1) {
break;
}
$array = $carry;
$currentDepth++;
}
Try this code:
<?php
$array = Array (
0 => Array (
0 => Array (
0 => Array ( ) ,
1 => Array ( ) ,
) ,
1 => Array ( ) ,
) ,
1 => Array (
0 => Array (
0 => Array (
0 => Array ( ),
1 => Array ( ),
),
),
) ,
);
function countTotalArr($arr, $lvl)
{
$result = array();
$countOnLevel = count($arr);
$result[$lvl] = $countOnLevel;
$tempArray = array();
foreach($arr as $index => $singleArray)
{
foreach($singleArray as $singleSubArray)
if(is_array($singleSubArray))
$tempArray[] = $singleSubArray;
}
if(!empty($tempArray))
{
$levelTemp = $lvl + 1;
$result = array_merge($result, countTotalArr($tempArray, $levelTemp));
}
return $result;
}
$total = countTotalArr($array, 0);
echo '<pre>';
print_r($total);
Result of print_r($total) is:
Array
(
[0] => 2
[1] => 3
[2] => 3
[3] => 2
)

How to group keys and values of an (sub)array and sum its values using PHP? [duplicate]

This question already has answers here:
Group array data on one column and sum data from another column
(5 answers)
Closed 9 months ago.
I have the following array
Array (
[0] => Array
(
[0] => ALFA
[1] => 213
)
[1] => Array
(
[0] => ALFA
[1] => 151
)
[2] => Array
(
[0] => ALFA
[1] => 197
)
[3] => Array
(
[0] => BETA
[1] => 167
)
[4] => Array
(
[0] => ZETA
[1] => 254
)
[5] => Array
(
[0] => GAMA
[1] => 138
)
[6] => Array
(
[0] => GAMA
[1] => 213
)
)
And I would like to group the key[0] of the subarray so I can see how many equal keys it has.
Something like that:
ALFA => 3
BETA => 1
EPSI => 1
GAMA => 2
I tried with array_count_values, but without success.
foreach ($array as $value) {
echo '<pre>';
print_r(array_count_values($value));
echo '</pre>';
}
With that we have following result:
Array
(
[ALFA] => 1
[213] => 1
)
Array
(
[ALFA] => 1
[151] => 1
)
...
Array
(
[GAMA] => 1
[213] => 1
)
And after that I would like to sum the values of each group as well.
ALFA => 213 + 151 + 197
BETA => 167
ZETA => 254
GAMA => 138 + 213
I think that when we solve the first part of the problem, the second would follow easier with quite the same method.
The final purpose is to divide the sum of values by the number of occurrences of each key group, so we can have an average of the values just like that:
ALFA => (213+151+197) / 3 = 187
BETA => 167
ZETA => 254
GAMA => (138+213) / 2 = 175,5
This is not the main problem, but as I said, I'm stuck with the beginning of the solution and would appreciate any help.
I'm surprised at all the long and complicated answers. However, the initial foreach to model your data to something manageable is needed. After that you just need to do a really simple array_walk.
<?php
$result = array();
foreach ($array as $el) {
if (!array_key_exists($el[0], $result)) {
$result[$el[0]] = array();
}
$result[$el[0]][] = $el[1];
}
array_walk($result, create_function('&$v,$k', '$v = array_sum($v) / count($v);'));
?>
Result:
Array
(
[ALFA] => 187
[BETA] => 167
[ZETA] => 254
[GAMA] => 175.5
)
Solution for you is here:
Code:
$input = [
['alfa', 123],
['alfa', 223],
['alfa', 122],
['alfa', 554],
['alfa', 34],
['dalfa', 123],
['halfa', 223],
['dalfa', 122],
['halfa', 554],
['ralfa', 34]
];
$result = [];
foreach ($input as $node) {
if (isset($result[$node[0]])) {
$result[$node[0]] = ['sum' => $result[$node[0]]['sum'] + $node[1], 'count' => $result[$node[0]]['count'] + 1];
} else {
$result[$node[0]] = ['sum' => $node[1], 'count' => 1];
}
}
print_r($result);
foreach ($result as $key => &$data) {
$data = $data['sum'] / $data['count'];
}
print_r($result);
Output:
Array
(
[alfa] => Array
(
[sum] => 1056
[count] => 5
)
[dalfa] => Array
(
[sum] => 245
[count] => 2
)
[halfa] => Array
(
[sum] => 777
[count] => 2
)
[ralfa] => Array
(
[sum] => 34
[count] => 1
)
)
Array
(
[alfa] => 211.2
[dalfa] => 122.5
[halfa] => 388.5
[ralfa] => 34
)
$sort = array();
foreach ($array as $value) {
$sort[$value[0]][] = $value[1];
}
then you count how many keys each has
$keys = array();
foreach($sort as $k => $v) {
$keys[$k] = count($v);
}
then for calculating the amount
$sum = array();
$average = array();
foreach($sort as $k => $v) {
$amount = 0;
foreach($v as $val) {
$amount += $val;
}
$sum[$k] = $amount;
$average[$k] = $amount / $keys[$k];
}
HOWEVER, If you want all the details in one array:
$final = array();
foreach ($array as $value) {
$final[$value[0]]["values"][] = $value[1];
}
foreach($final as $k => $v) {
$final[$k]["amount"] = count($v['values']);
$amount = 0;
foreach($v['values'] as $val) {
$amount += $val;
}
$final[$k]["sum"] = $amount;
$final[$k]["average"] = $amount / $final[$k]["amount"];
}
example: http://jdl-enterprises.co.uk/sof/25789697.php
Includes Output
Just copy the codes to your favorite text editor, sure it works perfectly.
$items = [
['ALFA',213],
['ALFA',151],
['ALFA',197],
['BETA',167],
['ZETA',254],
['GAMA',138],
['GAMA',213]
];
echo '<pre>' . print_r($items,true) . '</pre>';
$result;
foreach ($items as $value) {
# code...
if (isset($result[$value[0]])) {
$sum = $result[$value[0]]['sum'] + $value[1];
$count = $result[$value[0]]['count'] + 1;
$result[$value[0]] = ['sum' => $sum , 'count' => $count, 'divided' => ($sum / $count)];
} else {
$result[$value[0]] = ['sum' => $value[1] , 'count' => 1 , 'divided' => ($value[1] / 1) ];
}
}
echo '<pre>' . print_r($result,true) . '</pre>';
$myArray = [
["ALFA",213],
["ALFA",151],
["ALFA",197],
["BETA",167],
["ZETA",254],
["GAMA",138],
["GAMA",213]
];
$a1 = array(); //TEMPORARY ARRAY FOR KEEPING COUNT & TOTAL VALUES
$res = array(); //ARRAY USED TO KEEP RESULT
foreach($myArray as $val)
{
//AVOID PESKY NOTICES FOR UNDEFINED INDEXES
if ( !array_key_exists($val[0],$a1) ) {
$a1[$val[0]] = array("count" => 0,"total" => 0);
$res[$val[0]] = 0;
}
//INCREMENT THE COUNT OF INSTANCES OF THIS KEY
$a1[$val[0]]["count"]++;
//INCREMENT THE TOTAL VALUE OF INSTANCES OF THIS KEY
$a1[$val[0]]["total"]+=$val[1];
// UPDATE RESULT ARRAY
$res[$val[0]] = $a1[$val[0]]["total"] / $a1[$val[0]]["count"];
}
print_r($res);
Should result in:
Array
(
[ALFA] => 187
[BETA] => 167
[ZETA] => 254
[GAMA] => 175.5
)
Sample: http://phpfiddle.org/lite/code/a7nt-5svf

Foreach through array, do some calculations within the array, then create new array and merge back into original

Having some difficulty trying to do this. I'm fetching an array of all submissions, and they are being returned in an array like this:
Array
(
[0] => Array
(
[Submission] => Array
(
[user_id] => 2
[title] => This is a test, only a test
[source] => http://www.testing.com
[slug] => this-is-a-test-only-a-test
[category] => misc
[created] => 2012-10-05 03:43:11
[id] => 110
)
[SubmissionsVote] => Array
(
[0] => Array
(
[id] => 336
[user_id] => 2
[submission_id] => 110
[vote_type] => up
)
)
(
[1] => Array
(
[id] => 337
[user_id] => 4
[submission_id] => 110
[vote_type] => down
)
)
)
)
My intent is to loop through that returned array, and grab all the SubmissionsVote by vote_type == up and vote_type == down.
I then want to calculate a score (this example would return a score of 0 which is just: up - down. With that, I want to put that score into an array and append it to the end of the original one so it'd look like:
Array
(
[0] => Array
(
[Submission] => Array
(
[user_id] => 2
[title] => This is a test, only a test
[source] => http://www.testing.com
[slug] => this-is-a-test-only-a-test
[category] => misc
[created] => 2012-10-05 03:43:11
[id] => 110
)
[SubmissionsVote] => Array
(
[0] => Array
(
[id] => 336
[user_id] => 2
[submission_id] => 110
[vote_type] => up
)
)
(
[1] => Array
(
[id] => 337
[user_id] => 4
[submission_id] => 110
[vote_type] => down
)
)
[SubmissionScore] => Array
(
[0] => 0
)
)
)
Here is what I am doing unsuccessfully:
$votes = array();
$totalScore = array();
foreach ($submissions as $submission) {
$vote = $submission['SubmissionsVote'];
array_push($votes, $vote);
}
for ($i = 0; $i < count($votes); $i++) {
$downVotes = 0;
$upVotes = 0;
if ($votes[$i]['vote_type'] == 'down') {
$downVotes += 1;
} else if ($votes[$i]['vote_type'] == 'up') {
$upVotes += 1;
}
$score = $upVotes - $downVotes;
$totalScore = array_push($totalScore, $score);
}
Would love to get a little push in the right direction here.
Try this one:
$newSubmissions = array();
foreach ($submissions as $submission) {
$SubmissionsVote = $submission['SubmissionsVote'];
foreach($SubmissionsVote as $votes) {
$score = 0;
if ($votes['vote_type'] == 'down') {
$score -= 1;
} else if ($votes['vote_type'] == 'up') {
$score += 1;
}
$submission['SubmissionScore'] = array($score);
}
$newSubmissions[] = $submission;
}
$submissions = $newSubmissions;
I see at least two errors in your code :
You never set SubmissionScore in your array to $totalScore value.
Even if you add a $votes[$i]['SubmissionScore'] = $totalScore, that will not work, in PHP only object are passed by reference so $votes will only contain copy.
There are multiple possibility to resolve the problem but a reference in the foreach is a good choice :
foreach ($submissions as &$submission) {
$totalScore = 0;
foreach ($submission['SubmissionsVote'] as $vote) {
if ($vote['vote_type'] == 'down') {
$totalScore--;
} else if ($vote['vote_type'] == 'up') {
$totalScore++;
}
}
$submission['SubmissionScore'] = $totalScore;
}
Each submission will now have a SubmissionScore based on up/down vote.
You are initializing $downVotes and $upVotes inside a loop, that means you override values each time you iterate through the array.
Anyway, you could simplify this.
foreach ($submissions as $index => $submission) {
$up = 0;
$down = 0;
foreach ($submission['SubmissionsVote'] as $vote) {
if ($vote['vote_type'] == 'up') {
$up++;
} else {
$down++;
}
}
$submissions[$index]['SubmissionScore'] = $up - $down;
}

Categories