Sum data in multi-dimensional arrays with variable number of grouping keys - php

I have data coming back from MySQL and the goal is to pull data from multiple servers.
The data can be grouped by one or more fields, and I want to pull data from X number of MySQL servers and sum the data that should be summed, using the other data as a key or index.
Example:
Result from Mysql[$x]:
[{"date":"2017-10-10","account":"1","trees":"1","people":"0","pets":"4"}
,{"date":"2017-10-10","account":"2","trees":"2","people":"5","pets":"1"}
,{"date":"2017-10-11","account":"1","trees":"3","people":"3","pets":"4"}
,{"date":"2017-10-11","account":"2","trees":"4","people":"1","pets":"4"}]
Result from Mysql[$x+1]:
[{"date":"2017-10-10","account":"1","trees":"5","people":"1","pets":"1"}
,{"date":"2017-10-10","account":"2","trees":"5","people":"2","pets":"1"}
,{"date":"2017-10-11","account":"1","trees":"5","people":"0","pets":"2"}
,{"date":"2017-10-11","account":"2","trees":"5","people":"1","pets":"2"}]
Desired end result:
[{"date":"2017-10-10","account":"1","trees":"6","people":"1","pets":"5"}
,{"date":"2017-10-10","account":"2","trees":"7","people":"7","pets":"2"}
,{"date":"2017-10-11","account":"1","trees":"8","people":"3","pets":"6"}
,{"date":"2017-10-11","account":"2","trees":"9","people":"2","pets":"6"}]
in this case, there are 2 keys, date and account, but in reality there can be between 1 and 4.
The grouping keys are known, so the code knows that in this case, date and account are the keys.
I'm trying to find a better way than iterating through each row of mysql[$x] and then iterating through each row of mysql[$x+1] ... mysql[$x+n] and checking each value of the known keys to see if they match the current row's key values and then summing the data.
Also note that in reality, there may not be a row for each set of keys, hence the number and order of rows from each server can be variable.
I need an efficient way to do this because there are potentially tens of thousands of rows with a dozen+ columns.
I'm experimenting with code like this:
$res = array();
$keys = array('date','account');
array_walk($t,'rehash',$res);
function rehash($data,$key,&$result) {
global $keys;
global $res;
$keydata = array_slice($data,0,count($keys));
$vals = array_slice($data,count($keys));
//this is how the data would ideally be structured, my model
//$res[$keydata[$keys[0]]][$keydata[$keys[1]]] = $vals;
//this code below successfully creates rows in the right format
for($x = count($keys) - 1; $x>= 0; $x--) {
if($x == count($keys)-1) {
$tmp = array($keydata[$keys[$x]] => $vals);
//} else if($x ==0) {
// maybe reconcile here?
} else {
$tmp = array($keydata[$keys[$x]] => $tmp);
}
}
}
But how do i reconcile this? I can't simply do $res[key1] = $tmp[key1] because with multiple keys it would remove all the other rows.
Also with nested arrays, how would I iterate through each nested array if the number of levels is variable?
Lastly if I do this, I would need to convert it back into the original format for the frontend, so... joy. :D (this part would be easy, just is worth taking into consideration from an efficiency standpoint)

You don't need to use array_walk, since your data isn't hierarchical. Just use an ordinary foreach loop.
$res = array();
foreach ($t as $row) {
$cur = &$res;
// Drill down into result array using the key fields, creating nested arrays
// as necessary
foreach ($keys as $keycol) {
$key = $row[$keycol];
if (!isset($cur[$key])) {
$cur[$key] = array();
}
$cur = &$cur[$key];
}
// Now go through the data columns in the row, adding them to the totals
foreach ($row as $key => $val) {
if (in_array($key, $keys)) {
// Put the key fields in the result row
$cur[$key] = $val;
} else {
// Accumulate other values in the result row
if (!isset($cur[$key])) {
$cur[$key] = 0;
}
$cur[$key] += $row[$key];
}
}
unset($cur); // Break the reference link
}

Here is a possible solution based on the structure of the sample data given in the question.
$jsn1 = '[{"date":"2017-10-10","account":"1","trees":"1","people":"0","pets":"4"}
,{"date":"2017-10-10","account":"2","trees":"2","people":"5","pets":"1"}
,{"date":"2017-10-11","account":"1","trees":"3","people":"3","pets":"4"}
,{"date":"2017-10-11","account":"2","trees":"4","people":"1","pets":"4"}]';
$jsn2 = '[{"date":"2017-10-10","account":"1","trees":"5","people":"1","pets":"1"}
,{"date":"2017-10-10","account":"2","trees":"5","people":"2","pets":"1"}
,{"date":"2017-10-11","account":"1","trees":"5","people":"0","pets":"2"}
,{"date":"2017-10-11","account":"2","trees":"5","people":"1","pets":"2"}]';
/* Decode the json strings into associative arrays: */
$arr1 = json_decode($jsn1, true);
$arr2 = json_decode($jsn2, true);
/* Put in an array the keys whose values are to be added: */
/* Do not include date and account keys if they are not to be added */
$arr_keys = ['trees', 'people', 'pets'];
/* Loop through both $arr1 and $arr2 while storing new arrays in arr: */
/* Be adding up the values for the keys that are in $arr_keys */
$arr = [];
foreach($arr1 as $k => $v){
$arr[$k]['date'] = $arr1[$k]['date']; $arr[$k]['account'] = $arr1[$k]['account'];
foreach($arr_keys as $y){
if($arr1[$k][$y] !== null && $arr2[$k][$y] !== null){
$arr[$k][$y] = $arr1[$k][$y] + $arr2[$k][$y];
}}}
/* encode the array to the required json string: */
$json = json_encode($arr);
echo $json;
/* Output:
[{"date":"2017-10-10","account":"1","trees":"6","people":"1","pets":"5"}
,{"date":"2017-10-10","account":"2","trees":"7","people":"7","pets":"2"}
,{"date":"2017-10-11","account":"1","trees":"8","people":"3","pets":"6"}
,{"date":"2017-10-11","account":"2","trees":"9","people":"2","pets":"6"}]
*/

Related

Insert values in database after json_encode()

I have two dimensional array, and I got needed result.
Now I need to insert it into db.
If the situation is [[]], that is one row in db and has specific number in db (it should start with 1).
If the situation is [[] , []], in that case values in brackets are dimension, each bracket is row, but they have the same name, for ex 2.
If there are more brackets inside [[] , [], [], []], in this case we will have 4 rows with the same name.
My database looks like
ID | DIMENSIONS | NAME
ID - auto increment
DIMENSIONS I take from below.
For ex.
[[12500,10]] - is one row, and for NAME it will have number 1.
[[12500,8],[6400,2]] - this is six element in array below. It will have two rows in database, and will have NAME 6.
echo json_encode($pak); // produces output below
"[[[12500,10]],[[12500,10]],[[12500,10]],[[12500,10]],[[12500,10]],[[12500,8],[6400,2]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[5558,10]],[[5558,10]],[[5558,8],[4600,2]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,6],[4500,4]],[[4500,10]],[[4500,10]],[[4500,10]],[[4500,10]],[[4500,8]]]"
I got stuck here, any help is appreciated.
We can achieve it by Recursion.
But here I didn't use recursion (Because I am not good at that)
I haven't tested properly, so please go through it carefully. let me know if it has any problem.
$data = "[[[12500,1]],[[12500,2]],[[12500,3]],[[12500,4]],[[12500,5]],[[111111,6],[22222,6]],[[6400,7]],[[6400,8]],[[6400,9]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[6400,10]],[[5558,10]],[[5558,10]],[[5558,8],[4600,2]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,10]],[[4600,6],[4500,4]],[[4500,10]],[[4500,10]],[[4500,10]],[[4500,10]],[[4500,8]]]";
$decoded = json_decode($data);
$namedArray = fix_name($decoded);
$insertArray = arrange_array($namedArray);
echo "<pre>";
print_r($insertArray);
echo "</pre>";die;
// Fix the Name first
function fix_name($decoded)
{
$number = 1;
foreach ($decoded as $key=> $value) {
$value['name'] = $number;
$insertArray[] = $value;
$number++;
}
return $insertArray;
}
function arrange_array($namedArray)
{
$insertArray = [];
foreach ($namedArray as $array) {
if (count($array) > 2) {
$name = $array['name'];
unset($array['name']);
foreach ($array as $key => $value) {
$array[$key]['name'] = $name;
}
foreach ($array as $arr) {
$insertArray[] = arrange($arr);
}
} else {
$temp['DIMENSIONS '] = $array[0];
$temp['NAME'] = $array['name'];
$insertArray[] = $temp;
}
}
return $insertArray;
}
function arrange($array)
{
$temp = [];
$temp['DIMENSIONS'] = [$array[0], $array[1]];
$temp['NAME'] = $array['name'];
return $temp;
}
Process:
First: Fix the names
Next: If count > 2 (because "name" key was added in previous function so will have minimum two )
then array in different way
else
key 0 as DIMENSIONS and key name as NAME
NOTE: There are other ways to achieve it but for quick fix I gave this.

Get a field from an Array of records

How do I get all the values from a field in a record in an Array?
I want to make an average from a numerical score held under a specific field in an Array. The array is pulled from a form. Here is what I have so far but it just doesn't work.
$records = get_field('maths_month_report'); //gets all the records
$progressscore = $records['progress']; //targets the specific field in those records
echo . array_sum($progressscore)/count($progressscore) .; //divides the numbers in the field by the amount of records
revision: My apologies for not mentioning that the records are Arrays within the 'maths_month_report' Array. I can target specific fields in specific records with:
$firstrecord = $records[0];
$output = $firstrecord['progress'];
I just want to target all the 'progress' values so i can average them.
Based on your comments, if these are arrays within an array, you would need to build a new array:
$progresscore = array_column($records, 'progress')
PHP < 5.5:
$progresscore = array_map(function($item) {
return $item['progress'];
}, $records);
Or, just iterate yourself:
$sum = $count = 0;
foreach ($records as $record) {
$sum += $record['progress'];
++$count;
}
echo $sum / $count;

fetching from DB and store them in a new array PHP

I am fetching data from my DB and they look like this:
[{"0":"1","key-1":"1","1":"1","key-2":"1","2":"1","key-3":"1","3":"1","key-4":"1"}]
where the key-1 are the name of the column. (I only have one entry so).
I want to extract only the column values and save them into a new array that will output like this:
{"key-1":"1","key-2":"1","key-3":"1","key-4":"1"}
I want it to look exactly like this and not : [{"key-1":"1","key-2":"1","key-3":"1","key-4":"1"}]
I tried this:
$cart["key-1"]=$output["key-1"];
where $output is the outcome of the DB that shown first (the one with []).
and the $cart is the new array I want.
Both are declared as:
$cart=array();
$output=array();
and $output[]=$row where row is the result of the DB fetch. How to do it?
Here's one way to do it, I've substituted the database row for a string here, and made use of json_decode() and json_encode()
$data = '[{"0":"1","key-1":"1","1":"1","key-2":"1","2":"1","key-3":"1","3":"1","key-4":"1"}]';
// convert to an array
$data = json_decode($data, true);
// create new array here
$cart = array();
for ($i = 0; $i < count($data); $i++)
{
foreach ($data[$i] as $k => $v)
{
if (strpos($k, 'key') !== FALSE)
{
$cart[$k] = $v;
}
}
}
echo $cart['key-1'] . '<br/>';
echo json_encode($cart);
Output:
1
{"key-1":"1","key-2":"1","key-3":"1","key-4":"1"}
This is a very chaotically asked question.
From what I gathered you want maybe this..?
$cart=array();
foreach ($output as $index=>$value){
if stripos($index,"key-"){
cart[$index]=$value;
}
}
Use mysql_fetch_assoc() to get only column names ;)
while ($row = mysql_fetch_assoc($result_of_query))
{
echo $row['key-1'];
echo $row['key-2'];
}

json_encode array and other data

I have no trouble at all outputting MySQL data to an array then encoding in json.
What I am trying to understand is how can I add to this json output other pieces of non-dynamic data.
if (mysql_num_rows($result) > 0) {
while($obj = mysql_fetch_object($result)) {
$arr[] = $obj;
}
echo $_GET['callback'].'('.json_encode($arr).')';
}else{
}
What I need for example is to add something like
"firstnumber":"0"
"secondnumber":"10"
Is there a way to successfully add this form of data with the array of results and encode it all?
You can mix/match PHP arrays. The array you build with the database fetch loop will be given numeric keys (0, 1, 2, ...), and then you can add the 'firstnumber' and 'secondnumber' keys yourself afterwards.
however, this makes iterating the loop a bit tricky later, as you'll have to differentiate between the key types. However, nothing stops you from doing a nested structure:
$data = array();
while(...) {
$data['stuff_from_db'][] = $obj;
}
$data['other_stuff']['first_number'] = 0;
$data['other_stuff']['second_number'] = 10;
which allows you to keep the database results and the "other stuff" in parallel separate branches of the array.
I would create an array with my other data, and then append it to the end:
if (mysql_num_rows($result) > 0) {
while($obj = mysql_fetch_object($result)) {
$arr[] = $obj;
}
// Add other data
$otherData = Array('firstnumber'=>0, 'secondnumber'=>10);
// Append other data to end of array
$arr[] = $otherData;
echo $_GET['callback'].'('.json_encode($arr).')';
}else{
}

How to use arrays to store values fetch from sql query in php

What I want to do here is to be able to assign the values from sql query into arrays.
And then compute for the total.
<?php
include('conn.php');
$qreports=query_database("SELECT S_PRICE, PID, QTY_PUR, ITEM_ID, CUSID, CNUM, Cust_Name, TDATE FROM prod_table, customer_credit, sales_trans_items WHERE prod_table.PID=sales_trans_items.ITEM_ID AND sales_trans_items.CNUM=customer_credit.CUSID AND sales_trans_items.TDATE='2011-02-06 09:14:09'","onstor",$link);
$grandtotal=0;
$customer="";
while($qrep=mysql_fetch_assoc($qreports)){
$pid=$qrep['PID'];
foreach ($pid as $k => $v) {
$sids=$v;
$qtypur=$qrep['QTY_PUR'][$k];
$sprice=$qrep['S_PRICE'][$k];
$customer=$qrep['Cust_Name'][$k];
$tdate=$qrep['TDATE'][$k];
$stot=$qtypur * $sprice;
$grandtotal =$grandtotal + $stot;
}
}
echo "Customer: ". $customer."<br/>";
echo "Profit: ". $grandtotal;
?>
I tried the query on phpmyadmin before I place it in the code, and it output this:
I think you misunderstood how mysql_fetch_assoc works: Every call returns one row from the result set. $grep is an array with structure columnname => value.
That also means that $qrep['PID'] cannot be an array and your foreach loop won't work. In every loop, $qrep['PID'] contains one of the values you see in the PID column in your screen shot.
I suggest you add print_r($qreq); in your while loop so you get more familiar with the structure of the array.
As each $qrep is an array itself, creating a result array is just adding each of these to a new array:
$result = array();
while(($qrep=mysql_fetch_assoc($qreports))) {
$result[] = $qrep;
}
Now you are performing some more computation where I am not sure what you want to get in the end but it seems you are multiplying each QTY_P with S_PRICE:
$total = 0;
foreach($result as $report) {
$total += $report['QTY_P'] * $report['S_PRICE'];
}
(You could however also compute this in your while loop)
Assuming you want to get the overall sum and the sum for each customer:
$total = 0;
$total_per_customer = array();
foreach($result as $report) {
$customer = $report['Cust_Name'];
if(!array_key_exists($customer, $total_per_customer)) {
$total_per_customer[$customer] = 0;
}
$price = $report['QTY_P'] * $report['S_PRICE'];
$total_per_customer[$customer] += $price;
$total += $price;
}
In the end, $total_per_customer is an array with customer names as keys and the sum of the prices as values. $total will contain the sum of all prices.
You question is a bit unclear, but i think the answer wont be far a way from the following suggested references:
As you loop through the rows use
something like $array[] =
$qrep['columnName'] to populate an
array.
Then, check out PHP's array_sum().

Categories