Average result of multidimensional array in PHP - php

This is a question for all the array specialists out there. I have an multi dimension array with a result as number (can be 0,1 or 2) and need the average for each grouped by parent.
In the example below the calculation would be:
subentry1_sub1 = 2 + 2 = 4 (4/2=2)
subentry1_sub2 = 1 + 1 = 2 (2/2=1)
So what I try to archive in PHP is the following result:
subentry1_sub1 average = 2
subentry1_sub2 average = 1
...
I already tried some solutions from similar questions. But with all the recursive functions I didn't managed to get it aggregated by the last child name (e.g. subentry1_sub1).
Any ideas?
EDIT:
subentry1_sub1 is 2 + 2 because its two times in the array
[entry1] => [subentry1] => [subentry1_sub1] => result
[entry2] => [subentry1] => [subentry1_sub1] => result
Array
(
[entry1] => Array
(
[subentry1] => Array
(
[subentry1_sub1] => Array
(
[value] => abc
[result] => 2
)
[subentry1_sub2] => Array
(
[value] => abc
[result] => 1
)
)
[subentry2] => Array
(
[subentry2_sub1] => Array
(
[value] => abc
[result] => 1
)
[subentry2_sub2] => Array
(
[value] => abc
[result] => 1
)
)
)
[entry2] => Array
(
[subentry1] => Array
(
[subentry1_sub1] => Array
(
[value] => abc
[result] => 2
)
[subentry1_sub2] => Array
(
[value] => abc
[result] => 1
)
)
[subentry2] => Array
(
[subentry2_sub1] => Array
(
[value] => abc
[result] => 1
)
[subentry2_sub2] => Array
(
[value] => abc
[result] => 1
)
)
)
)

Try this code. In this i have created a new array $sum which will add result value of same subentry childs with same key and another array $count which will count the number of times each key repeats
<?php
$data = array('entry1'=>array(
'subentry1'=>
array(
'subentry1_sub1'=>array('value'=>'abc','result'=>2),
'subentry1_sub2'=>array('value'=>'abc','result'=>1)
),
'subentry2'=>
array(
'subentry2_sub1'=>array('value'=>'abc','result'=>1),
'subentry2_sub2'=>array('value'=>'abc','result'=>1)
)
),
'entry2'=>array(
'subentry1'=>
array(
'subentry1_sub1'=>array('value'=>'abc','result'=>2),
'subentry1_sub2'=>array('value'=>'abc','result'=>1)
),
'subentry2'=>
array(
'subentry2_sub1'=>array('value'=>'abc','result'=>1),
'subentry2_sub2'=>array('value'=>'abc','result'=>1)
)
)
);
$sum = array();
$repeat = array();
foreach($data as $input){
foreach($input as $array){
foreach($array as $key=>$value){
if(array_key_exists($key,$sum)){
$repeat[$key] = $repeat[$key]+1;
$sum[$key] = $sum[$key] + $value['result'];
}else{
$repeat[$key] = 1;
$sum[$key] = $value['result'];
}}}}
echo "<pre>";
print_r($sum);
print_r($repeat);
foreach($sum as $key=>$value){
echo $key. ' Average = '. $value/$repeat[$key]."</br>";
}
Output
Array
(
[subentry1_sub1] => 4
[subentry1_sub2] => 2
[subentry2_sub1] => 2
[subentry2_sub2] => 2
)
Array
(
[subentry1_sub1] => 2
[subentry1_sub2] => 2
[subentry2_sub1] => 2
[subentry2_sub2] => 2
)
subentry1_sub1 Average = 2
subentry1_sub2 Average = 1
subentry2_sub1 Average = 1
subentry2_sub2 Average = 1
You can easily calculate avg now
Note : As you mentioned you are counting occurence of subentry1_sub1 etc so i did the same so it will also count whether key result exists or not

I know this is an old thread but im pretty sure there is a much easier way of doing this for anyone who is interested:
If you know the result will always be a number:
foreach($my_array as $entry_name => $entry_data)
{
foreach($entry_data as $sub_name => $sub_data)
{
$sub_results = array_column($sub_data, 'result');
$averages[$entry_name][$sub_name] = array_sum($sub_results)/count($sub_results);
}
}
If its possible the result could be NULL or empty, this will check it and return 'N/A' if there is no valid data to calculate an average from:
foreach($my_array as $entry_name => $entry_data)
{
foreach($entry_data as $sub_name => $sub_data)
{
$sub_results = array_filter(array_column($sub_data, 'result'));
$averages[$entry_name][$sub_name] = (count($sub_results) > 0 ? array_sum($sub_results)/count($sub_results) : 'N/A');
}
}
both of these solutions will give you an averages array that will output the average per subentry per entry.

Try this like,
<?php
$data=array('entry1'=>array(
'subentry1'=>
array(
'subentry1_sub1'=>array('value'=>'abc','result'=>3),
'subentry1_sub2'=>array('value'=>'abc','result'=>3)
),
'subentry2'=>
array(
'subentry2_sub1'=>array('value'=>'abc','result'=>2),
'subentry2_sub2'=>array('value'=>'abc','result'=>8)
)
),
'entry2'=>array(
'subentry1'=>
array(
'subentry1_sub1'=>array('value'=>'abc','result'=>6),
'subentry1_sub2'=>array('value'=>'abc','result'=>6)
),
'subentry2'=>
array(
'subentry2_sub1'=>array('value'=>'abc','result'=>10),
'subentry2_sub2'=>array('value'=>'abc','result'=>12)
)
)
);
foreach($data as $k=>$v){
echo "----------------$k---------------------\n";
if(is_array($v)){
foreach($v as $a=>$b){
if(is_array($b)){
echo $a.' average = ';
$c=array_keys($b);// now get *_sub*
$v1=isset($b[$c[0]]['result']) ? $b[$c[0]]['result'] : '';
$v2=isset($b[$c[1]]['result']) ? $b[$c[1]]['result'] : '';
echo ($v1+$v2)/2;
echo "\n";
}
}
}
}
Online Demo

In the meantime I found a simple working solution myself:
foreach ($data as $level2) {
foreach ($level2 as $level3) {
foreach ($level3 as $keyp => $level4) {
foreach ($level4 as $key => $value) {
if($key == 'result') $stats[$keyp] += $value;
}
}
}
}
With that you get the total for every key in an new array $stats.
But be sure to checkout the solution from user1234, too. It's working great and already includes the calculation of the average.
https://stackoverflow.com/a/39292593/2466703

Related

PHP - Conditioned sum of some values of an array

I have an array in PHP of this type, resulting from a particular query on a db.
$output = Array
(
[0] => Array
(
'price' => 100
'date' => '2015-07-28'
'total' => 200
'qty' => 2
)
[1] => Array
(
'price' => 80
'date' => '2015-07-28'
'total' => 240
'qty' => 3
)
[2] => Array
(
'price' => 100
'date' => '2015-07-29'
'total' => 300
'qty' => 3
)
[3] => Array
(
'price' => 90
'date' => '2015-07-28'
'total' => 90
'qty' => 1
)
)
I'm trying to sum total and qty based on price key values, obtaining an array that will look like this:
$grouped = Array
(
[0] => Array
(
[price] => 100
[sumtotal] => 500
[sumqty] => 5
)
[1] => Array
(
[price] => 80
[sumtotal] => 240
[sumqty] => 3
)
[2] => Array
(
[price] => 90
[sumtotal] => 90
[sumqty] => 1
)
)
Still cannot find a way to get around this.
Try using simple foreach loop as
$result = array();
foreach ($output as $key => $value) {
$hash = $value['price'];
if (isset($result[$hash])) {
$result[$hash]['price'] = $value['price'];
$result[$hash]['sumtotal'] += $value['total'];
$result[$hash]['sumqty'] += $value['qty'];
} else {
$result[$hash]['price'] = $value['price'];
$result[$hash]['sumtotal'] = $value['total'];
$result[$hash]['sumqty'] = $value['qty'];
}
}
print_r(array_values($result));
Demo
I don't understand, it's the same array without the date.. ?
Edit :
Ok try this
$temp= array();
for($i=0; $i<sizeof($outpout);$i++)
{
if(!isset($temp[$i]))
$temp[$i]=array();
$temp[$i][$outpout[$i]['price']]+=$outpout[$i]['qty']];
}
$res=array();
$count=0;
foreach($temp as $key => $value)
{
$res[$count]['price']=$key;
$res[$count]['qty']=$value;
$res[$count]['total']=$key*$value;
$count++;
}
Okay, here's a simple complete code to solve your problem.
$output=array();//this is your array
$price_index=array();
$final_array=array();
foreach($output as $key=>$value)
{
//first check if price is already in $price_index
if(!isset($price_index[$value["price"]]))
{
$price_index[$value["price"]]=sizeof($price_index);
}
$final_index=$price_index[$value["price"]];
if(isset($final_array[$final_index]))
{
$final_array[$final_index]["total"]+=$value["total"];
$final_array[$final_index]["qty"]+=$value["qty"];
}
else
{
$final_array[$final_index]["total"]=$value["total"];
$final_array[$final_index]["qty"]=$value["qty"];
$final_array[$final_index]["price"]=$value["price"];
}
}
//display our final array
print_r($final_array);
What I did:
iterate through your array
check if the current price has been added to $final_array before, by creating $price_index array that contains price as key and the value is the index of $output array on the first time the price occurred.
now if the price occur again, the $price_index[current price] will be detected that it is already been set before, then we will use the value as the index of our $final_array and add the current total and qty to existing.
then display the array (optional).
Hope it helps. Please don't just copy my code, analyze and understand it mate :)

From php multidimensional array trying to create nested ul li menu (unlimited nested levels)

Here is what i have got http://codepad.org/iDoXXsLX
Have array like this
Array
(
[0] => Array
(
[NumberRenamed] => 17
[TopicName] => Products
[UpperLevelNumberRenamed] => 0
)
[17] => Array
(
[0] => Array
(
[1] => Array
(
[NumberRenamed] => 18
[TopicName] => Computers
[UpperLevelNumberRenamed] => 17
)
)
)
[18] => Array
(
[0] => Array
(
[2] => Array
(
[NumberRenamed] => 16
[TopicName] => Laptops
[UpperLevelNumberRenamed] => 18
)
)
)
[16] => Array
(
[0] => Array
(
[4] => Array
(
[NumberRenamed] => 8
[TopicName] => Dell
[UpperLevelNumberRenamed] => 16
)
)
)
)
Top level item is Products, first sub-level item is Computers, next sub-level is Laptops, then again next sub-level Dell
For each sub-level item UpperLevelNumberRenamed == to closest upper level NumberRenamed.
Want to get result like this
Products
Computers
Laptops
Dell
Acer
Desktops
Home
Tried this
foreach( $main_topics as $k_main_topics => $v_main_topics ){
if( isset($v_main_topics['UpperLevelNumberRenamed']) and $v_main_topics['UpperLevelNumberRenamed'] == 0 ){
//print only top level topics
echo $v_main_topics['TopicName']. '<br/>';
}
else{//if not top level topic
foreach( $v_main_topics[0] as $k_v_main_topics_0 => $v_v_main_topics_0 ){
echo $v_v_main_topics_0['TopicName']. '<br/>';
}//foreach( $v_main_topics[0] as $k_v_main_topics_0 => $v_v_main_topics_0 )
}//else{
}//foreach( $main_topics as $k_main_topics => $v_main_topics )
But get this
Products
Home
Computers
Laptops
Desktops
Dell
Acer
Something incorrect, but can not understand what. Please, advice what need to correct/change in the code
Trying another way
Initial array is one dimensional array. Trying to get ul li navigation from one dimensional.
Here is what i did http://codepad.org/OLtxyL4X
Here's a summary of what it does:
flatten the array recursively
build a multi-dimensional relation map
create 1D relationships that link UpperLevelNumberRenamed to NumberRenamed
print out the multi-dimensional as an ul-li list.
Here it is:
$flat = array();
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($main_topics)) as $i)
$flat[] = $i;
$final = array();
$defs = array();
for ($i = 0; $i < count($flat); $i += 3)
if ($flat[$i + 2] == 0) {
$final[$flat[$i + 1]] = array();
$defs[$flat[$i]] = &$final[$flat[$i + 1]];
} else {
$defs[$flat[$i + 2]][$flat[$i + 1]] = array();
$defs[$flat[$i]] = &$defs[$flat[$i + 2]][$flat[$i + 1]];
}
function array2ul($array) {
$out = "<ul>";
foreach($array as $key => $elem)
$out = is_array($elem) ?
$out . "<li><span>$key</span>" . array2ul($elem) . "</li>" :
$out = $out."<li><span>$key:[$elem]</span></li>";
$out = $out . "</ul>";
return $out;
}
echo array2ul($final);
Output:
<ul><li><span>Products</span><ul><li><span>Computers</span><ul><li><span>Laptops</span><ul><li><span>Dell</span><ul></ul></li><li><span>Acer</span><ul></ul></li></ul></li><li><span>Desktops</span><ul></ul></li></ul></li></ul></li><li><span>Home</span><ul></ul></li></ul>
This shall be a working example using recursion, not tested though:
Define the array
$main_array = Array
(
'10' => Array
(
'name' => 'Products'
'children' => Array
(
'12' => Array
(
'name' => 'Laptop',
'children' => Array
(
'13' => Array
(
'name' => 'Dell',
),
'14' => Array
(
'name' => 'Acer',
)
)
)
'14' => Array
(
'name' => 'Desktop',
'children' => Array
(
'15' => Array
(
'name' => 'Sony',
),
'16' => Array
(
'name' => 'Apple',
)
)
),
)
)
)
Create and call the function :
function createList($main_topics)
{
if($main_topics == null || sizeof($main_topics) <= 0)
{
return '';
}
$list = '<ul>';
foreach($main_topics as $k_main_topics => $v_main_topics )
{
$list .= '<li id="' . $k_main_topics'"> '. $v_main_topics['name'] . ' ' . createList(isset($v_main_topics["children"]) ? $v_main_topics["children"] : null) . '</li>' ;
}
$list .= '</ul>';
return $list;
}
echo createList($main_array);

Comparing in an Array: If two highest value exists, then compare the other value

Suppose I have an array like:
array( [0] => array([item]=>apple [buy]=>50 [sell]=>30)
[1] => array([item]=>lemon [buy]=>50 [sell]=>60)
[2] => array([item]=>banana [buy]=>40 [sell]=>20)
[3] => array([item]=>orange [buy]=>20 [sell]=>30)
)
Currently I am using this script to check which item has the most buyer
function getMax($array, $val)
{
$max = 0;
foreach( $array as $k => $v )
{
$max = max( array( $max, $v[$val] ) );
}
return $max;
}
$highestBuy = getMax($thisArray, 'buy');
foreach($thisArray as $i=>element){
if($element['buy'] == $highestBuy){
$thisArray[$i]['highestBuy'] = 'yes';
} else {
$thisArray[$i]['highestBuy'] = 'no';
}
}
In this case, both apple and lemon will have highestBuy a yes value. But now I want to find out which item is the most popular by checking their sell if there are two or more same value of highestBuy. Which is the most simple or fastest way to make the output like:
array([0] => array([item]=>apple [buy]=>50 [sell]=>30 [mostPopular]=>no)
[1] => array([item]=>lemon [buy]=>50 [sell]=>60 [mostPopular]=>yes)
[2] => array([item]=>banana [buy]=>40 [sell]=>20 [mostPopular]=>no)
[3] => array([item]=>orange [buy]=>20 [sell]=>30 [mostPopular]=>no)
)
Thanks in advance.
EDIT:
What I want to do is:
find out the highest buy
If this value occur only once(which means there are one highest buy in the array) then push the [mostPouplar]=>yes into the array
If not(there are two or more same highest value), then find out the highest sell.
That's mean if the highest value is unique, it will stop doing further action. If not, it will keep going to find secondary highest value in an array. Is it possible to achieve this?
Sort array with your rules and take first element
$array = array( '0' => array('item'=>apple, 'buy'=>50 ,'sell'=>30),
'1' => array('item'=>lemon, 'buy'=>50, 'sell'=>60),
'2' => array('item'=>banana, 'buy'=>40, 'sell'=>20),
'3' => array('item'=>orange, 'buy'=>20 ,'sell'=>30)
);
usort($array,
function($a, $b) {
$res = $b['buy'] - $a['buy'];
if (!$res) $res = $b['sell'] - $a['sell'];
return $res; });
result:
Array (
[0] => Array ( [item] => lemon [buy] => 50 [sell] => 60 )
[1] => Array ( [item] => apple [buy] => 50 [sell] => 30 )
[2] => Array ( [item] => banana [buy] => 40 [sell] => 20 )
[3] => Array ( [item] => orange [buy] => 20 [sell] => 30 ) )
I had changed the getMax() to return the index of the most popular item
function getMax($array, $val, $val2)
{
$max_item = 0;
foreach( $array as $k => $v )
{
if($array[$max_item][$val] <= $v[$val] && $array[$max_item][$val2] <= $v[$val2])
$max_item = $k;
}
return $max_item;
}
$highestBuy = getMax($thisArray, 'buy', 'sell');
foreach($thisArray as $i => $element){
$thisArray[$i]['mostPopular'] = ($i == $highestBuy) ? 'yes' : 'no';
}

Sum Multi Dimensional array if the sub key is exists

I have this array which has more then 100 results but some of these has same sub array key. I would like to sum array element which has same key which is [/xyx/888350] in this example. However, I want to keep the format as it is which is two dimensional.
Array
(
[0] => Array
(
[/xyx/888350] => /xyx/888350
[visitors] => 1
[pageviews] => 2
[uniquepageviews] => 1
)
[1] => Array
(
[/xyx/888350] => /xyx/888350
[visitors] => 1
[pageviews] => 3
[uniquepageviews] => 1
)
[2] => Array
(
[/xyx/888350] => /xyx/888350
[visitors] => 1
[pageviews] => 2
[uniquepageviews] => 1
)
[3] => Array
(
[/xyx/102254] => /xyx/102254
[visitors] => 1
[pageviews] => 2
[uniquepageviews] => 1
)
)
I am expecting out put something like below:
Array
(
[0] => Array
(
[/xyx/888350] => /xyx/888350
[visitors] => 2
[pageviews] => 7
[uniquepageviews] => 2
)
[1] => Array
(
[/xyx/102254] => /xyx/102254
[visitors] => 1
[pageviews] => 3
[uniquepageviews] => 1
)
)
Thanks in advance.
Loop the array, and store the results in a temporary array, by using the first value as a key:
$input = /*your example data here*/;
$result = array();
foreach($input as $data){
$keys = array_keys($data);
$key = $keys[0]; //get the first key of the array a.k.a '/xyx/888350'
if(isset($result[$key])){
//sum the values if we have this key
$result[$key]['visitors'] += $data['visitors'];
$result[$key]['pageviews'] += $data['pageviews'];
$result[$key]['uniquepageviews'] += $data['uniquepageviews'];
}else{
$result[$key] = $data;
}
}
//drop the extra keys and return a indexed array with the summed values
return array_values($result);
$results=$service->data_ga->get("");
$stats = array();
for ($i=0; $i < count($stats_results=$results->getRows()) ; $i++)
{
$stats[]=array(
'path'=>$stats_results[$i][1],
'visitors'=>$stats[$i]['visitors'] + $stats_results[$i][2],
'pageviews'=>$stats[$i]['visitors'] + $stats_results[$i][3],
'uniquepageviews'=>$stats[$i]['visitors'] + $stats_results[$i][4]
);
}
$result = array();
foreach($stats as $data){
$keys = array_keys($data);
$key = $keys[0]; //get the first key of the array a.k.a '/xyx/888350'
if(isset($result[$key]))
{
//sum the values if we have this key
$result[$key]['visitors'] += $data['visitors'];
$result[$key]['pageviews'] += $data['pageviews'];
$result[$key]['uniquepageviews'] += $data['uniquepageviews'];
}else{
$result[$key] = $data;
}
}
echo "<pre>";
print_r($result);
Output:
Array
(
[path] => Array
(
[path] => /path/888350
[visitors] => 3
[pageviews] => 7
[uniquepageviews] => 3
)
)
Thanks both of you. As time goes on I will also answer questions other people have. I have just started my career. Thank you guys :)

How to correctly convert an array in PHP with quantity/price data?

I can't seem to wrap my head around this.
I am given an array in PHP that looks something like this:
array (
0 => array (
0 => 50,
1 => 0.80
),
1 => array (
0 => 300,
1 => 0.50
),
2 => array (
0 => 600,
1 => 0.30
),
3 => array (
0 => 1000,
1 => 0.20
),
4 => array (
0 => 4000,
1 => 0.10
)
);
An array of arrays where the first index of the inner array represents a quantity while the second index represents a price.
I want to import this data into my database, but in a specific way.
I have specific quantities that I like to keep track of that are defined by the following array:
array(10,100,500,1000,5000,10000);
I then want to make the original array more fine tuned to quantities and prices that I would like to see. So in this particular example, I would like an array that looks like this:
array (
0 => array (
0 => 100,
1 => 0.80
),
1 => array (
0 => 500,
1 => 0.50
),
2 => array (
0 => 1000,
1 => 0.20
),
3 => array (
0 => 5000,
1 => 0.10
)
);
My new array will only contain the specific quantity indexes.
If a quantity exists in the original array, I use that price. If it doesn't exist, I would use the price of the next lowest quantity. If no lower quantity exists, I don't want to see that quantity in the new array.
I have been able to accomplish what I want for the most part with the following code:
function getRelativePrices($pricearray) {
$relativeprices = array();
$types = array(10,100,500,1000,5000,10000);
foreach ($types as $q) {
$new_array = array();
foreach ($pricearray as $index => $array) {
if ($q >= $array[0]) {
$new_array = array($q, $array[1]);
}
}
if (sizeof($new_array)) {
$relativeprices[] = $new_array;
}
}
return $relativeprices;
}
The only problem with the above is that I am getting extra data that I do not want. In the example I provided, I am getting a 5th index/array at the end that looks like:
4 => array (
0 => 10000,
1 => 0.10
)
I don't want this last piece, since I find it redundant considering that I know that 5000 pieces cost $0.10 each, so I can assume that 10000 will cost the same price when "4000" is the highest quantity given in the original array.
So I want to ask for help in removing this last piece.
Also, I was wondering if someone had a better coding method in general for converting this array.
You could just do in your inner foreach:
foreach ($pricearray as $index => $array) {
if ($q >= $array[0]) {
if($q == 10000) { continue; }
$new_array = array($q, $array[1]);
}
}
OK I think this should do the trick. I think the problem was in your comparison... See code:
function getRelativePrices($pricearray) {
$relativeprices= array();
$types = array(10,100,500,1000,5000,10000);
foreach($pricearray as $p) {
$new_array = array();
foreach($types as $t) {
if($p[0] <= $t) {
$new_array = array($t,$p[1]);
break;
}
}
if(sizeof($new_array)) {
$relativeprices[] = $new_array;
}
}
return $relativeprices;
}
Here is an example of my test based on your code examples:
function getRelativePrices($pricearray) {
$relativeprices= array();
$types = array(10,100,500,1000,5000,10000);
foreach($pricearray as $p) {
$new_array = array();
foreach($types as $t) {
if($p[0] <= $t) {
$new_array = array($t,$p[1]);
break;
}
}
if(sizeof($new_array)) {
$relativeprices[] = $new_array;
}
}
return $relativeprices;
}
$test = array (
0 => array (
0 => 50,
1 => 0.80
),
1 => array (
0 => 300,
1 => 0.50
),
2 => array (
0 => 600,
1 => 0.30
),
3 => array (
0 => 1000,
1 => 0.20
),
4 => array (
0 => 4000,
1 => 0.10
)
);
print_r(getRelativePrices($test));
And the output was:
Array
(
[0] => Array
(
[0] => 100
[1] => 0.8
)
[1] => Array
(
[0] => 500
[1] => 0.5
)
[2] => Array
(
[0] => 1000
[1] => 0.3
)
[3] => Array
(
[0] => 1000
[1] => 0.2
)
[4] => Array
(
[0] => 5000
[1] => 0.1
)
)

Categories