PHP - multi-dimensional array overwriting first value - php

I'm creating a multi-dimensional array to match the hierarchy of a company. I ask the database for the downline (saved in a seperate table) and start looping it. Currently I'm only doing this for the highest level and the people below him. I know he has 6 people below him, thus the end result only shows one. When I var_dump $complete every time in the loop, I see different values in the children-array. (AKA he overwrites instead of adds up).
I've tried both array_push and this method, I hope I've been clear enough, because I probably don't (sorry.)
The code:
foreach ($downpositions as $downposition) {
// start position
if ($downposition['PositionsDownposition']['positionsdownpositions_rel_position'] == 1) {
// check downline only on first level
if ($downposition['PositionsDownposition']['positionsdownpositions_level'] == 1) {
foreach ($downpositions as $mother) {
if ($mother['PositionsDownposition']['positionsdownpositions_rel_downposition'] == 1) {
// format it
$node = array(
'id' => $downposition['PositionsDownposition']['positionsdownpositions_id'],
'name' => $downposition['PositionsDownposition']['positionsdownpositions_rel_position'],
'children' => array()
);
$complete = array(
'id' => $mother['PositionsDownposition']['positionsdownpositions_id'],
'name' => $mother['PositionsDownposition']['positionsdownpositions_rel_position'],
'children' => array()
);
// add up to array
$complete['children'][] = $node;
}
}
}
}
}

Found the problem, im intialising $complete['children'] in the loop, thus resetting the array to empty all the time.
Solution:
Re-do the code, initialise of $complete outside the loop.

Related

Create hierarchical / parent-child array using loops and references instead of recursion

I have a list of objects, each object could belong as a child of another object and each one has an element ('parent_id') that specifies which object it's a child of.
An example of what the expected tree should look like after children are properly imbedded:
Current Array
element 1
element 2
element 3
element 4
element 5
Organized into a hierarchy
-----------------------
element 2
-children:
element 1
-children
element 5
element 3
-children:
element 4
There should be two top elements, the first element would contain the element1 as a child, which would itself contain element 5 as a child. The third element would contain element 4 as a child.
The final result should produce a proper tree structure from it, however I'm running into an issue that when I generate the tree some of the elements are null.
From what I understand it is because of the weird scoping rules PHP has and that the last reference in the loop remains bound even after the end of the loop. So I included the unset($var) statements after each loop, however I'm still getting null values pushed into the tree.
Here is the fully contained example of the code:
$response = array(
array( "kind"=> "t1", "data" => array( "id" => 25, "parent_id" => 30 )),
array("kind"=> "t1", "data" => array( "id" => 30,"parent_id" => 0)),
array("kind"=> "t1", "data" => array("id" => 32, "parent_id" => 0 )),
array("kind"=> "t1", "data" => array("id" => 33,"parent_id" => 32)),
array("kind"=> "t1", "data" => array("id" => 35,"parent_id" => 25))
);
$json_str = json_encode($response);
$tree = array();
foreach($response as &$firstObj)
{
$parentFound = null;
foreach($response as &$secondObject)
{
if($firstObj['data']['parent_id'] == $secondObject['data']['id'])
{
$parentFound = &$secondObject;
break;
}
}
unset($secondObject);
if($parentFound)
{
$parentFound['data']['children'] = array(&$firstObj);
}
else{
$tree[] = $firstObj;
}
}
unset($firstObj);
print_r($tree);
The expected tree should contain only the topmost elements that are not children of other elements, the children should be embedded through references into the appropriate spaces of the top tree elements.
While I would probably opt for a recursive approach on a professional project, I've managed to produce a non-recursive approach using references like your posted code but with a single loop.
Code: (Demo)
$result = [];
foreach ($array as $obj) {
$id = $obj['data']['id'];
$parentId = $obj['data']['parent_id'];
if (isset($ref[$id])) { // child array populated before parent encountered
$obj['children'] = $ref[$id]['children']; // don't lose previously stored data
}
$ref[$id] = $obj;
if (!$parentId) {
$result[] = &$ref[$id]; // push top-level reference into tree
} else {
$ref[$parentId]['children'][] = &$ref[$id]; // push into parent-specific collection of references
}
}
var_export($result);
For a stacking process (not a recursive one) to build a hierarchical structure, your sample data is a little unexpected in that a child id integer is less than a parent id. I mean, in nature, parents are born first and then children are born. With your sample data, the input could not be pre-sorted by id value before looping -- this would have ensured that all parents where declared before children were encountered.
As a consequence, my snippet needs to push previously encountered children data into a newly encountered parent so that the declaration of the parent as a reference does not erase the cached children data.
I extended your sample data by one extra row while testing. If you find any breakages with my script, please supply new test data that exposes the issue.
I found the solution with the help of GPT/Bing:
So while I was unsetting the other variables, I wasn't unsetting the $parentFound, which has to be done as well.
Another thing was that I was not saving the item by reference when saving the item to the tree, which also has to be done in order for the whole reference tree to work.
The final code is:
$json_str = json_encode($response);
$tree = array();
foreach($response as &$firstObj)
{
$parentFound = null;
foreach($response as &$secondObject)
{
if($firstObj['data']['parent_id'] == $secondObject['data']['id'])
{
$parentFound = &$secondObject;
break;
}
}
if($parentFound)
{
$parentFound['data']['children'] = array(&$firstObj);
}
else{
$tree[] = &$firstObj; //has to be passed by reference
}
unset($secondObject);
unset($parentFound); //have to also include the $parentFound in the unset
}
unset($firstObj);
print_r($tree);

How to pass data from a master array to multiple arrays in PHP

I have an array with subarrays, each subarray has 'type' (int) and a 'datetime'. I need to loop trought the array and create new arrays (or objects) with groups of these subarrays in a special way: every 'type' could be [0-3] 0 means startdate and 3 means enddate (1 and 2 are in between for other purposes). Every new array with these subarrays should start with 0 and end with 3.
Im not using any framework, only PHP5 and jQuery. The master array comes from a loop I made from a SQL query with GROUP_CONCAT. I just passed the concat fields to a master array but I need to regroup the subarrays to create some type of registries.
Here is the array I got with subarrays. They are already sorted by datetime, every type '0' means "start date / start new registry" and '3' means "end date / end registry".
$array = [
["tipo"=>0,"fechahora"=>"2019-05-26 09:35:30"],
["tipo"=>1,"fechahora"=>"2019-05-26 10:30:15"],
["tipo"=>2,"fechahora"=>"2019-05-26 10:43:12"],
["tipo"=>3,"fechahora"=>"2019-05-26 14:30:26"],
["tipo"=>0,"fechahora"=>"2019-05-26 15:35:22"],
["tipo"=>3,"fechahora"=>"2019-05-26 16:35:31"],
["tipo"=>0,"fechahora"=>"2019-05-27 08:31:57"],
["tipo"=>1,"fechahora"=>"2019-05-27 10:27:22"],
["tipo"=>2,"fechahora"=>"2019-05-27 10:38:31"],
["tipo"=>3,"fechahora"=>"2019-05-27 14:20:38"],
["tipo"=>0,"fechahora"=>"2019-05-28 09:39:42"],
["tipo"=>1,"fechahora"=>"2019-05-28 11:43:08"],
["tipo"=>2,"fechahora"=>"2019-05-28 11:53:19"],
["tipo"=>3,"fechahora"=>"2019-05-28 14:43:31"],
["tipo"=>0,"fechahora"=>"2019-05-29 10:30:22"],
["tipo"=>3,"fechahora"=>"2019-05-29 14:38:46"]
];
I need to create a new array with subarrays or objects by registry, a registry is an element who have startdate (type 0), somedates in between (type 1,2) and a enddate (type 3). Its important The next subarray have the startdate more current than the older subarray enddate. Every "tipo"+"fechahora" array have more fields (signature, address, etc) so I need to keep these as subarrays:
$newarray = [
'registry0' => [
"startdate"=> ["tipo"=>0,"fechahora"=>"2019-05-26 09:35:30"],
"pauses"=> [
["tipo"=>1,"fechahora"=>"2019-05-26 10:30:15"],
["tipo"=>2,"fechahora"=>"2019-05-26 10:43:12"]
],
"enddate" => ["tipo"=>3,"fechahora"=>"2019-05-26 14:30:26"]
],
'registry1' => [
"startdate"=> ["tipo"=>0,"fechahora"=>"2019-05-26 15:35:22"],
"pauses"=> [],
"enddate" => ["tipo"=>3,"fechahora"=>"2019-05-26 16:35:31"]
],
'registry2' => [
"startdate"=> ["tipo"=>0,"fechahora"=>"2019-05-27 08:31:57"],
"pauses"=> [
["tipo"=>1,"fechahora"=>"2019-05-27 10:27:22"],
["tipo"=>2,"fechahora"=>"2019-05-27 10:38:31"]
],
"enddate" => ["tipo"=>3,"fechahora"=>"2019-05-27 14:20:38"]
]
];
I could use arrays or objects, I dont know hot to develop a loop to regroup arrays like this (starting at one field value and ending with another and so on). I dont even know if there is a simpler way. Any help would be appreciated.
If this could help: I need the registries this way to put into an HTML table, each registry in a row.
EDIT:
Iknow I have to use a loop but I dont know how to "get first element with type 0, create an array, include element, include all other element until type 3, close array, create new array and so on..."
In some code:
$newarray = [];
foreach($array as $element) {
if ($element["tipo"]==0) {
//new subarray
//include this in subarray
}
// include all "tipos" 1,2 in subarray
if ($element["tipo"]==3) {
//include this in subarray
//finish subarray
}
//incude subarray in $newarray
}
return $newarray;
I dont know how to continue.
For this case a simple foreach followed by switch case will do,
$result = [];
$i = 0;
$var = 3; // you can manage this at your will
foreach ($array as $key => $value) {
switch ($value['tipo']) { // passing tipo value here
case 0:
// it will capture for same record for current value of $i
$result['registry' . $i]['startdate'] = $value;
break;
case $var:
// detects last entry and will increment
$result['registry' . $i++]['enddate'] = $value;
break;
default:
// if not 0 and $var then push it in pauses
$result['registry' . $i]['pauses'][] = $value;
break;
}
}
Demo Link.
It is just a case of looping over each of the original array values and creating an entry in a temporary array ($temp in this case) with any values according to type. Then when a type 3 is found, adding this to the $newarray...
$newarray = [];
$temp = [];
$count = 0;
foreach ( $array as $entry ) {
if ( $entry['tipo'] == 0 ) {
$temp = [ 'startdate' => $entry ];
}
else if ( $entry['tipo'] == 3 ) {
$temp["enddate"] = $entry;
$newarray["registry".$count++] = $temp;
}
else {
$temp["pauses"][] = $entry;
}
}

Convert multi-dimensional array into matrix-like array

I have the following array:
$array = array(
array("2018","2019"),
"Jan",
array("France","Germany")
);
I need a matrix that crosses all the elements of the array; e.g:
array(
array("2018","Jan","France"),
array("2018","Jan","Germany"),
array("2019","Jan","France"),
array("2019","Jan","Germany")
);
meaning, 2 x 2 x 1 arrays
but this can be that I have more elements that are or are not arrays then:
$array = array(
array("2018","2019"),
"Jan",
array("France","Germany"),
array("prod1","prod2","prod3"),
'Act'
);
In this case I would get 2 x 2 x 1 x 4 x 1 arrays in the end.
Any idea on how to achieve this?
Is that what you are looking for ?
$dst = array(array());
foreach ($array as $idx => $val) {
foreach ($dst as $tmp_idx => $tmp_array) {
if (is_array($val)) {
foreach ($val as $sub_idx => $sub_val) {
$dst[] = array_merge($dst[$tmp_idx], array(count($dst[$tmp_idx]) => $sub_val));
}
} else {
$dst[] = array_merge($dst[$tmp_idx], array(count($dst[$tmp_idx]) => $val));
}
unset($dst[$tmp_idx]);
}
}
I declare the array with an empty array in it.
A first foreach iterates through the main array to access all the categories, whatever their number.
A second foreach iterates through the result array.
A third foreach - or not, depending if the category contains several or a single entry - iterates through each entry of the current category and merges an array containing only that current category in the result currently iterated on (from $dst). The merge is pushed into the result array. The result stored in $dst are assumed to be temporary and will be copied and completed with each new value, making them progressively contain one entry from each category.
At the end of each iteration on $dst, the current temporary result is removed from the array since it has no purpose : if the foreach iterated on it, that means that it was incomplete.
At the end, the only entries in $dst that remain are these who weren't iterated on, meaning that every entry from each category were considered when they were created, meaning that they are complete.
I'm available for any clarification may the need arise.

Match specific words in a string and categorize them

I'm pretty new to PHP so bear with me here. I'm trying to iterate through the words in a string of text, look for specific words, categorize them, and then count the number of times each word category was hit. I was able to do the easy part but I'm having problems counting the number of times each category is matched. Here's the main function that accepts my string:
public function matchThemeTest($query){
$marriageNum = 0;
$criminalNum = 0;
$contactNum = 0;
$keywords = array(
'background'=> array('category'=>'criminal'),
'marriage' => array('category'=>'marriage'),
'criminal' => array('category'=>'criminal'),
'arrest' => array('category'=>'criminal'),
'divorce' => array('category'=>'marriage'),
'person' => array('category'=>'contact'),
'contact' => array('category'=>'contact')
);
foreach (preg_split("/\s/", $query) as $word)
{
if (isset($keywords[$word]))
{
echo $keywords[$word]['category'];
if ($keywords[$word]['category'] == 'marriage') {
$marriageNum++;
}
echo $marriageNum;
}
}
//return reset($matches);
}
I've got a php fiddle setup here: http://phpfiddle.org/main/code/i4g-mdu that I've been playing around with. In it's current form, I can get the words into categories but I'm not sure how to count how many times each category gets matched. I feel like I need an additional loop or something simple to count but I'm not exactly sure where. Any advice is greatly appreciated. Thanks in advance.
You may need another array of data, to store the counts. Use an array like this:
$counts = array(
'criminal' => 0,
'marriage' => 0,
'contact' => 0
);
Then when you iterate through your foreach loop, you can use the $keywords[$word]['category'] as the key in $counts and increment it:
if(isset($keywords[$word]) {
$counts[$keywords[$word]['category']]++;
}
Then you can return the $counts array so the caller can use it to find out what the counts of each theme were:
return $counts;

PHP: Session 2-Dimensional Array - Track Viewed Products

I'm trying to create an array to display the last 5 products a customer has viewed.
The array is a 2 dimensional array like below...
$RView= array(
array( ID => "1001", RefCode => "Ref_01", Name => "Name_01" ),
...
array( ID => "1005", RefCode => "Ref_05", Name => "Name_05" )
);
The array values are retrieved from the products recordset and is designed to function as follows when a customer visits a product page.
Page will check if a Session Array exists
If yes, an array variable is created from existing Session
If no, a new array is created.
Array will add the new product details.
Array will count if there are more than 5 existing products in the array.
If yes, it will remove the oldest.
If no, moves to next step.
A Session is created/updated from the revised Array.
My current effort is attached below...
Many thanks for any help.
<?php
session_start()
// Get or Create Array
IF (isset($_SESSION['sessRView'])) {
$RView = ($_SESSION['sessRView']); }
ELSE {
$RView = array(array());
}
// Append currently viewed Product to Array
array(array_unshift($RView, $row_rsPrd['PrdID'], $row_rsPrd['RefCode'], $row_rsPrd['Name']));
// Check if more than 5 products exist in Array, if so delete.
IF (sizeof($RView) > 5) {
array(array_pop($RView)); }
// Update Session for next page
$_SESSION['sessRView'] = $RView;
// Display Array
for ($row = 0; $row < 5; $row++)
{
echo "<ul>";
echo "<li><a href='?PrdID=".$RView[$row]["PrdID"]."'>".$RView[$row]["RefCode"]."</a> : ".$RView[$row]["Name"]."</li>";
echo "</ul>";
}
?>
It's more or less right - just 2 lines need to be changed.
There's no need for the extra array() around array_unshift and array_pop.
When you use array_unshift you're pushing an array of items (not the id/codes individually) - I think you mean array_unshift($RView, array($prodid,$name,...))
What if $RView doesn't have 5 elements? In that case you're accessing undefined array indices (which may or may not show an error). Change it to a foreach loop: e.g.
foreach ($Rview as $prod) echo $prod['Name']...
It should work after you make these changes. You might want to clean up the coding style a bit, though :)
EDIT: Oh, I see, when you're referencing the array in the for loop it doesn't know that the array has "ProdID" and "Name" indices. When you make an array you have to define the indexes using the => operator.
Add indexes to the array when you array_unshift:
array_unshift($RView, array("ProdID" => $row_rsProd["ProdID"], "Name"...))
If row_rsProd isn't too big, you can just tack the entire row_rsprod onto $RView.
so change array_unshift(...) to just $RView[] = $row_rsProd
This way the indexes are preserved.
Alternatively you can change the indicies in the for loop to match. Right now the array you unshift onto $RView is 0-based - $RView[0][0] is the product ID for the first product, etc.
So you can change the stuff in the foreach loop to
echo "<li>..." $prod[0] $prod[1] $prod[2]
Hope that helps!

Categories