What is the best way to group a php array? - php

For example, I have this array:
$bills = array(
array("bill_id"=>"1", "product_id"=>"1", "total"=>"10"),
array("bill_id"=>"2", "product_id"=>"2", "total"=>"20"),
array("bill_id"=>"3", "product_id"=>"1", "total"=>"30"),
array("bill_id"=>"4", "product_id"=>"1", "total"=>"40"),
array("bill_id"=>"5", "product_id"=>"2", "total"=>"50")
);
We need to add the totals of each produdct into a single array, i.e. What is the best clean fast way to generate the following array from the above one:
$products = array(
array("product_id"=>"1", "total"=>"80"),
array("product_id"=>"2", "total"=>"70")
);

the fastest way to sum this is index array, something like this
$products = array();
foreach ($bills as $bill) {
$key = $bill['product_id'];
if (isset($products[$key])) {
$products[$key]['total'] += $bill['total'];
} else {
$products[$key] = $bill;
}
}
var_dump($products);
output
array(2) {
[1]=>
array(3) {
["bill_id"]=>
string(1) "1"
["product_id"]=>
string(1) "1"
["total"]=>
int(80)
}
[2]=>
array(3) {
["bill_id"]=>
string(1) "2"
["product_id"]=>
string(1) "2"
["total"]=>
int(70)
}
}
to browse the invoice list
foreach($products as $key=>$bill) {
var_dump($bill);
}

Thie simplest approach is a single-pass loop.
$byProduct = [];
foreach($bills as $bill)
{
$key = $bill['product_id'];
if (!isset($byProduct[$key])) {
$byProduct[$key] = [
'product_id' => $key,
'total' => 0
];
}
$byProduct[$key]['total'] += $bill['total'];
}
Result of var_dump($byProduct):
array(2) {
[1] =>
array(2) {
'product_id' =>
string(1) "1"
'total' =>
int(80)
}
[2] =>
array(2) {
'product_id' =>
string(1) "2"
'total' =>
int(70)
}
}
Another approach is to use array_walk but it is pretty much the same in terms of complexity:
$byProduct = [];
array_walk($bills, function(&$bill) use (&$byProduct) {
$key = $bill['product_id'];
if (!isset($byProduct[$key])) {
$byProduct[$key] = [
'product_id' => $key,
'total' => 0
];
}
$byProduct[$key]['total'] += $bill['total'];
});

Related

PHP Array, if duplicate id concat values and delete duplicate

I have an array like this
array(2) {
["sys_ID"]=> string(32) "ab0ce921dba8a810f6db3892399619d9" ["sites"]=> array(5) {
[0]=> array(2) {
["sys_ID"]=> string(32) "448ce5a1dba8a810f6db3892399619ba" ["service"]=> string(4) "IDMB" } [1]=> array(2) {
["sys_ID"]=> string(32) "448ce5a1dba8a810f6db3892399619ba" ["service"]=> string(4) "ODMB" } [2]=> array(2) {
["sys_ID"]=> string(32) "598ce5a1dba8a810f6db3892399619bc" ["service"]=> string(4) "IDMB" } [3]=> array(2) {
["sys_ID"]=> string(32) "876ce5a1dba8a810f6db38923996199f" ["service"]=> string(4) "IDMB" } [4]=> array(2) {
["sys_ID"]=> string(32) "876ce5a1dba8a810f6db38923996199f" ["service"]=> string(4) "ODMB" } } }
If there is a duplicate ['sys_ID'] I want to change the first ['service'] => "IDMB,ODMB" then delete the duplicate value so there is only 1. So the above would become
array(2) {
["sys_ID"]=> string(32) "ab0ce921dba8a810f6db3892399619d9" ["sites"]=> array(5) {
[0]=> array(2) {
["sys_ID"]=> string(32) "448ce5a1dba8a810f6db3892399619ba" ["service"]=> string(4) "IDMB,ODMB" } [1]=> array(2) {
["sys_ID"]=> string(32) "598ce5a1dba8a810f6db3892399619bc" ["service"]=> string(4) "IDMB" } [2]=> array(2) {
["sys_ID"]=> string(32) "876ce5a1dba8a810f6db38923996199f" ["service"]=> string(4) "IDMB,ODMB" } ]} }
The first array was made getting POST values;
<?php
foreach ($_POST['services'] as $item) {
$parts = explode(',', $item);
$siteID = $parts[1];
$services = $parts[0];
$data['sites'][] = [
'sys_ID' => $siteID,
'service' => $services
];
}
?>
Change the way you generate your array to something like this:
<?php
$data = [
'sites' => [],
];
foreach ($_POST['services'] as $item) {
// Gather your data
$parts = explode(',', $item);
$siteID = $parts[1];
$services = $parts[0];
// Set flag to remember whether you need to add it
$add = true;
// Loop through existing data and check if the sys_ID already exists
foreach ($data['sites'] as &$dataDetails) {
// It does ... so just append your service
if ($dataDetails['sys_ID'] === $siteID) {
$add = false;
$dataDetails['service'] .= ',' . $services;
break;
}
}
// Couldn't find the sys_ID => Add new entry
if ($add) {
$data['sites'][] = [
'sys_ID' => $siteID,
'service' => $services
];
}
}
?>
It will be better to fill the array properly from the beginning; however I will still post a solution. We have the array defined like this:
$arr = array(
'sys_ID' => 'ab0ce921dba8a810f6db3892399619d9',
'sites' => array(
0 => array(
'sys_ID' => "448ce5a1dba8a810f6db3892399619ba",
'service'=> "IDMB"
),
1 => array(
'sys_ID' => "448ce5a1dba8a810f6db3892399619ba",
'service'=> "ODMB"
),
2 => array(
'sys_ID'=> "598ce5a1dba8a810f6db3892399619bc",
'service'=> "IDMB"
),
3 => array(
'sys_ID'=> "876ce5a1dba8a810f6db38923996199f",
'service'=> "IDMB"
),
4 => array(
'sys_ID'=> "876ce5a1dba8a810f6db38923996199f",
'service'=> "ODMB"
)
)
);
To merge the elements like described, you can do the following loop:
// For each site (1)...
for($i=0; $i<count($arr["sites"])-1; $i++){
// Take each of sites starting from the next one to the end (2)
for($j=$i+1; $j<count($arr["sites"]); $j++){
// If there is a match in sys_ID beteen (1) and (2)
if($arr["sites"][$i]['sys_ID'] == $arr["sites"][$j]['sys_ID']){
// Merge the service into (1) with comma separation
$arr["sites"][$i]['service'] = $arr["sites"][$i]['service'].",".$arr["sites"][$j]['service'];
// then delete (2)
array_splice($arr["sites"],$j,1);
$j--;
}
}
}
Note that this will reindex the numebers (1,2,3...). If you wish to preserve them, you can use unset() instead.

Traverse an STD Class object inside an array

I've send an Array with data through FormData with Ajax.
I json encoded it first before sending it through and decoded it back at the PHP side.
My problem is this: "Trying to get property 'ID' of non-object" Or "Illegal string offset ID"
which is weird, since my array is like:
array(7) {
[0]=>
object(stdClass)#779 (2) {
["ID"]=>
string(1) "1"
["Value"]=>
string(19) "Onbeperkt helpdesk."
}
[1]=>
object(stdClass)#780 (2) {
["ID"]=>
string(1) "2"
["Value"]=>
string(43) "Een direct aanspreekpunt voor al uw vragen."
}
[2]=>
object(stdClass)#781 (2) {
["ID"]=>
string(1) "3"
["Value"]=>
string(20) "Een stabiel netwerk."
}
[3]=>
object(stdClass)#782 (2) {
["ID"]=>
string(1) "4"
["Value"]=>
string(43) "Uw belangrijke gegevens optimaal beveiligd."
}
}
The way I tried traversing the array:
$voordelen = json_decode($_POST['voordelen']);
echo var_dump($voordelen);
for ($i = 0; $i < count($voordelen); $i++) {
foreach ($voordelen[$i] as $key => $item) {
$voorArray = array(
"id" => $item->ID,
"item" => $item->Value,
"content_id" => $content->id // variable from code not shown here.
);
}
}
I also tried:
for ($i = 0; $i < count($voordelen); $i++) {
foreach ($voordelen[$i] as $item) {
$voorArray = array(
"id" => $item->ID,
"item" => $item->Value,
"content_id" => $content->id // Variable from code not shown here.
);
}
}
I also tried using $var['value'] instead of $var->value
But now I'm at a loss. Any help would be appreciated.
I think you are making an extra loop, did you try:
foreach ($voordelen as $item) {
$voorArray = array(
"id" => $item->ID,
"item" => $item->Value,
"content_id" => $content->id // Variable from code not shown here.
);
var_dump($voorArray);
}
What's wrong with just looping through it, since $voordelen is already an array:
$voordelen = json_decode($_POST['voordelen']);
foreach( $voordelen as $item )
{
$voorArray = array(
"id" => $item->ID,
"item" => $item->Value,
"content_id" => $content->id
);
// Do something with $voorArray here.
}

Combining array values for the same associative key

I've got this array, and I want to loop through it and add up the values prices that are on the same OrderDate. The other values like the Discount code I want to add as a sub-array.
array(3) {
[0]=>
array(4) {
["OrderDate"]=>
string(10) "2018-01-01"
["DiscountCode"]=>
NULL
["TotalRevenue"]=>
string(9) "147618.76"
["Discount_Revenue"]=>
string(8) "13453.77"
}
[1]=>
array(4) {
["OrderDate"]=>
string(10) "2018-01-01"
["DiscountCode"]=>
string(6) "SALE38"
["TotalRevenue"]=>
string(8) "364.92"
["Discount_Revenue"]=>
string(8) "4083.64"
}
[2]=>
array(4) {
["OrderDate"]=>
string(10) "2018-01-01"
["DiscountCode"]=>
string(9) "WELCOME20"
["TotalRevenue"]=>
string(6) "113.83"
["Discount_Revenue"]=>
string(6) "113.83"
}
}
So it should then look like:
array(3) {
[0]=>
array(4) {
["OrderDate"]=>
string(10) "2018-01-01"
["DiscountCodes"]=> array {
[0] => "DISCOUNT"
[1] => "SALE38"
[2] => "WELCOME20"
)
["TotalRevenue"]=>
string(9) "147618.76"
["Discount_Revenue"]=>
string(8) "13453.77"
}
}
I believe I have fixed it using this loop adding to the array if the key exists. Not sure if this is the most efficient way to do it though?
foreach ($results as $k => $result){
if( array_key_exists($result['OrderDate'], $arr)){
$arr[$result['OrderDate']]['price'] += $result['TotalRevenue'];
$arr[$result['OrderDate']]['new'] = false;
} else {
$arr[$result['OrderDate']] = array(
'price' => $result['TotalRevenue'],
'new' => true
);
}
}
I've come to my own solution if anyone else needs it.
$arr = array();
foreach ($results as $k => $result){
if( array_key_exists($result['OrderDate'], $arr)){
$arr[$result['OrderDate']]['Total_Revenue'] += $result['TotalRevenue'];
$arr[$result['OrderDate']]['Discount_Revenue'] += $result['Discount_Revenue'];
isset($result['DiscountCode']) ? $arr[$result['OrderDate']]['Discount_Code'][] = $result['DiscountCode'] : '';
$arr[$result['OrderDate']]['new'] = false;
} else {
$arr[$result['OrderDate']] = array(
'Total_Revenue' => $result['TotalRevenue'],
'Discount_Revenue' => $result['Discount_Revenue'],
'new' => true
);
isset($result['DiscountCode']) ? $arr[$result['OrderDate']]['Discount_Code'][] = $result['DiscountCode'] : '';
}
}

Sort array by the value of the key

i'm trying to sort an array by the value of a sub-key in DESC order but I'm stuck.
I've could make it with ksort but it was in ascending order..
Here's my array :
array_by_lang => array(
[no] => array(
[3-1] => array(//some informations),
[3-10] => array(//informations),
[3-7] => array(//informations),
[5-1] => array(//informations)
)
)
what i want to obtain is something like :
array_by_lang => array(
[no] => array(
[5-1] => array(//informations),
[3-10] => array(//some informations),
[3-7] => array(//informations),
[3-1] => array(//informations)
)
)
Is that possible ? Thanks a lot
I think, you need "reversing natural sort by key". Just with array_multisort and array_reverse (see also natsort):
$array_by_lang = array(
'no' => array(
'3-1' => array('info_1'),
'3-10' => array('info_2'),
'3-7' => array('info_3'),
'5-1' => array('info_4'),
)
);
array_multisort(array_keys($array_by_lang['no']),SORT_NATURAL, $array_by_lang['no']);
$array_by_lang['no'] = array_reverse($array_by_lang['no']); // reverse natural order - "DESC"
var_dump($array_by_lang);
Output
array(1) {
["no"]=>
array(4) {
["5-1"]=>
array(1) {
[0]=>
string(6) "info_4"
}
["3-10"]=>
array(1) {
[0]=>
string(6) "info_2"
}
["3-7"]=>
array(1) {
[0]=>
string(6) "info_3"
}
["3-1"]=>
array(1) {
[0]=>
string(6) "info_1"
}
}
}
This might help -
$a = array(
'3-1' => array('//some informations'),
'3-10' => array('//informations'),
'3-7' => array('//informations'),
'5-1' => array('//informations')
);
## Array for keys
$temp= array();
foreach(array_keys($a) as $v) {
$t = explode('-', $v);
$temp[$t[0]][] = $t[1];
}
## Sort the keys
foreach($temp as &$ar) {
rsort($ar);
}
krsort($temp);
## Final array
$final= array();
foreach($temp as $k => $f) {
foreach($f as $v) {
$key = $k . '-' . $v;
$final[$key] = $a[$key];
}
}
var_dump($final);
Output
array(4) {
["5-1"]=>
array(1) {
[0]=>
string(14) "//informations"
}
["3-10"]=>
array(1) {
[0]=>
string(14) "//informations"
}
["3-7"]=>
array(1) {
[0]=>
string(14) "//informations"
}
["3-1"]=>
array(1) {
[0]=>
string(19) "//some informations"
}
}
DEMO

Creating a nested array form a flat array trough Parent ID

I want to make my flat array to a nested array but I can't seem to do this ;/
the code that I tryed to make it nested with:
<?php
$link = mysqli_connect("localhost", "db_phpnav", "jeXS9ftZhmJdzRWd", "db_phpNav");
$sql = "SELECT ID, PID, Naam FROM tb_nav";
$result = mysqli_query($link, $sql);
function convertToTree(array $flat, $idField = 'ID',
$parentIdField = 'PID',
$childNodesField = 'childNodes') {
$indexed = array();
foreach ($flat as $row) {
$indexed[$row[$idField]] = $row;
$indexed[$row[$idField]][$childNodesField] = array();
}
$root = null;
foreach ($indexed as $id => $row) {
$indexed[$row[$parentIdField]][$childNodesField][$id] =& $indexed[$id];
if (!$row[$parentIdField]) {
$root = $id;
}
}
return array($root => $indexed[$root]);
}
$rows = array();
while($row = mysqli_fetch_assoc($result)) {
$rows[] = $row;
}
convertToTree($rows);
?>
the array it gives out. it clearly doesn't do what I intended for.
array(3) {
[0]=>
array(3) {
["ID"]=>
string(1) "2"
["PID"]=>
NULL
["Naam"]=>
string(7) "Contact"
}
[1]=>
array(3) {
["ID"]=>
string(1) "5"
["PID"]=>
string(1) "2"
["Naam"]=>
string(7) "testing"
}
[2]=>
array(3) {
["ID"]=>
string(1) "6"
["PID"]=>
NULL
["Naam"]=>
string(8) "testing2"
}
}
How do i get the array to be nested nicely?
it should look more like this:
[0]=>
array(3) {
["ID"]=>
string(1) "2"
["PID"]=>
NULL
["Naam"]=>
string(7) "Contact"
'childNodes' => array(
2 => array(
["ID"]=>
string(1) "5"
["PID"]=>
string(1) "2"
["Naam"]=>
string(7) "testing"
'childNodes' => array ();
);
}
Suppose this is (kind of) what you want:
<?PHP
$entries = array();
$entries[] = array("ID" => "2", "PID" => NULL, "Naam" => "Contact");
$entries[] = array("ID" => "5", "PID" => "2", "Naam" => "testing");
$entries[] = array("ID" => "6", "PID" => NULL, "Naam" => "testing2");
function convertToTree($flat, $idField = 'ID', $parentIdField = 'PID', $childNodesField = 'childNodes', $curIdx = NULL)
{
$indexed = array();
foreach($flat as $row)
{
if ($row[$parentIdField] == $curIdx)
{
$indexed[$row[$idField]] = $row;
$indexed[$row[$idField]]["childNodes"] = convertToTree($flat, $idField, $parentIdField, $childNodesField, $row[$idField]);
}
}
return $indexed;
}
print_r($entries);
$tree = convertToTree($entries);
print_r($tree);
?>
/**
* Transform array to a nested structure.
* This gets a little complicated. Basically, we're creating a placeholder in
* $children to hold the child terms of each parent term. Then we create a
* reference to that element in the parent term. So nesting happens via magic.
*/
function arrayFlatToNested($array, $idKey = 'ID', $parentIdKey = 'PID', $childrenKey = 'childNodes'): array
{
$arrayNested = [];
$children = [];
foreach ($array as $element) {
// Create a placeholder for the object's children.
if (!isset($children[$element[$idKey]])) {
$children[$element[$idKey]] = [];
}
// Create a link to those children.
$element[$childrenKey] = &$children[$element[$idKey]];
if (!strlen($element[$parentIdKey])) {
$arrayNested[] = $element;
} else {
// Create a placeholder for the parent.
if (!isset($children[$element[$parentIdKey]])) {
$children[$element[$parentIdKey]] = [];
}
// Add this object to the parent, even if it doesn't exist yet.
$children[$element[$parentIdKey]][] = $element;
}
}
return $arrayNested;
}

Categories