PHP id/parent, how to show all records - php

I found sample PHP code for creating JSON based on id<->parent and it looks like:
<?php
$rows = array(
array('id' => 1, 'parent' => 0, 'name' => 'John Doe'),
array('id' => 2, 'parent' => 1, 'name' => 'Sally Smith'),
array('id' => 3, 'parent' => 2, 'name' => 'Mike Jones'),
array('id' => 4, 'parent' => 3, 'name' => 'Jason Williams'),
array('id' => 5, 'parent' => 4, 'name' => 'Sara Johnson'),
array('id' => 6, 'parent' => 1, 'name' => 'Dave Wilson'),
array('id' => 7, 'parent' => 2, 'name' => 'Amy Martin'),
);
// create an index on id
$index = array();
foreach($rows as $row){
$row['data'] = (object) [];
$index[$row['id']] = $row;
}
// build the tree
foreach($index as $id => &$row){
if ($id === 0) continue;
$parent = $row['parent'];
$index[$parent]['children'][] = &$row;
}
unset($row);
// obtain root node
$index = $index[0]['children'][0];
// output json
header('Content-Type: application/json');
echo json_encode($index, JSON_PRETTY_PRINT);
it works fine but how to display all children records for specific ID? For example, I need all members for parent 2 so I try:
$index = $index[2]['children'][0];
and it display only 3 records and it should display 4 of them (Amy Martin is missing).
Thank you.

It is because you are trying to get 0 index so only 1st hierarchy will be displayed. To access all children remove [0] from your data
It should be:
$index = $index[2]['children'];

You only get 3 records because your 'children' are nested on different levels (the 4th one is on a different level
Array
(
[0] => Array
(
[id] => 3
[parent] => 2
[name] => Mike Jones
...
[children] => Array
(
[0] => Array
(
[id] => 4
[parent] => 3
[name] => Jason Williams
[children] => Array
(
[0] => Array
(
[id] => 5
[parent] => 4
[name] => Sara Johnson
...
)
)
)
)
)
[1] => Array
(
[id] => 7
[parent] => 2
[name] => Amy Martin
...
)
)
In order to get all the children you need to traverse and flatten your array. This can be done using an interator.
Full code sample:
<?php
$rows = array(
array('id' => 1, 'parent' => 0, 'name' => 'John Doe'),
array('id' => 2, 'parent' => 1, 'name' => 'Sally Smith'),
array('id' => 3, 'parent' => 2, 'name' => 'Mike Jones'),
array('id' => 4, 'parent' => 3, 'name' => 'Jason Williams'),
array('id' => 5, 'parent' => 4, 'name' => 'Sara Johnson'),
array('id' => 6, 'parent' => 1, 'name' => 'Dave Wilson'),
array('id' => 7, 'parent' => 2, 'name' => 'Amy Martin'),
);
// create an index on id
$index = array();
foreach($rows as $row){
$row['data'] = (object) [];
$index[$row['id']] = $row;
}
// build the tree
foreach($index as $id => &$row){
if ($id === 0) continue;
$parent = $row['parent'];
$index[$parent]['children'][] = &$row;
}
unset($row);
$array = $index[2]['children'];
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$data = array();
foreach ($iterator as $leafValue) {
if ( !$iterator -> hasChildren() ) {
if ($iterator->key() == 'id') {
$data[$leafValue]['id'] = $leafValue;
$id = $leafValue;
} else{
$data[$id][$iterator->key()] = $leafValue;
}
}
}
// output json
header('Content-Type: application/json');
echo json_encode($data, JSON_PRETTY_PRINT);
Output
{
"3": {
"id": 3,
"parent": 2,
"name": "Mike Jones"
},
"4": {
"id": 4,
"parent": 3,
"name": "Jason Williams"
},
"5": {
"id": 5,
"parent": 4,
"name": "Sara Johnson"
},
"7": {
"id": 7,
"parent": 2,
"name": "Amy Martin"
}
}

Related

function to make Tree like Array in to Array

I have $tree like array which made by function from the $array I need a function to make it $tree to $array back
so I need a function to make it back.
lets call it $list this time.
I will share $tree , $array and the function in below
tree like array ;
Array
(
[0] => Array
(
[id] => 1
[name] => id1
[children] => Array
(
[0] => Array
(
[id] => 2
[parent_id] => 1
[name] => id2
[children] => Array
(
[0] => Array
(
[id] => 5
[parent_id] => 2
[name] => id5
)
)
)
[1] => Array
(
[id] => 3
[parent_id] => 1
[name] => id3
[children] => Array
(
[0] => Array
(
[id] => 6
[parent_id] => 3
[name] => id6
)
[1] => Array
(
[id] => 8
[parent_id] => 3
[name] => id8
)
)
)
)
)
[1] => Array
(
[id] => 4
[name] => id4
[children] => Array
(
[0] => Array
(
[id] => 9
[parent_id] => 4
[name] => id9
[children] => Array
(
[0] => Array
(
[id] => 10
[parent_id] => 9
[name] => id10
)
)
)
)
)
[2] => Array
(
[id] => 7
[name] => id7
[children] => Array
(
)
)
)
Which made by a function from this array
$array = [
['id'=> 1, 'parent_id' => 0, 'name' => 'id1'],
['id' => 2, 'parent_id' => 1, 'name'=> 'id2'],
['id' => 3, 'parent_id' => 1, 'name'=> 'id3'],
['id' => 4, 'parent_id' => 0, 'name'=> 'id4'],
['id' => 5,'parent_id' => 2, 'name'=> 'id5'],
['id' => 6, 'parent_id' => 3, 'name'=> 'id6'],
['id' => 7, 'parent_id' => 0, 'name'=> 'id7'],
['id' => 8, 'parent_id' => 3, 'name'=> 'id8'],
['id' => 9, 'parent_id' => 4, 'name'=> 'id9'],
['id' => 10, 'parent_id' => 9, 'name'=> 'id10'],
];
function(making $array in to $tree)
$tree = [];
function buildTree (array $infos, int $parent_Id = null): array
{
$branch = [];
foreach ($infos as $info)
if($info['parent_id'] === $parent_Id){
$children = buildTree($infos , $info['id']);
if ($children){
$info['children'] = $children;
}
$branch[] = $info;
}
return $branch;
}
foreach ($array as $info){
if($info['parent_id']=== 0){
$tree[] = [
'id' => $info['id'],
'name' => $info['name'],
'children' => buildTree($array , $info['id']),
];
}
}
print_r($tree);
any explanation would be appreciated.
Result of this code can be found here: http://sandbox.onlinephpfunctions.com/code/38a091db5ace63900fa0bf69ddde17412118513c
function flatMergeArray(array $array, int $parentId = 0, array &$result = []): array
{
$subResult = [];
foreach ($array as $key => $sub) {
$parentId = $array['parent_id'] ?? 0;
if (is_array($sub)) {
flatMergeArray($sub, $parentId, $result);
} else {
$subResult[$key] = $sub;
}
}
if (!empty($subResult)) {
if (!isset($subResult['parent_id'])) {
$subResult['parent_id'] = 0;
}
$result[] = $subResult;
}
return $result;
}
function flatTree(array $tree): array
{
$array = flatMergeArray($tree);
usort($array, static function (array $node1, array $node2) {
return ($node1['id'] < $node2['id']) ? -1 : 1;
});
return array_values($array);
}
$tree = [
[
"id" => 1,
"name" => "id1",
"children" => [
[
"id" => 2,
"parent_id" => 1,
"name" => "id2",
"children" => [
[
"id" => 5,
"parent_id" => 2,
"name" => "id5"
]
]
],
[
"id" => 3,
"parent_id" => 1,
"name" => "id3",
"children" => [
[
"id" => 6,
"parent_id" => 3,
"name" => "id6"
],
[
"id" => 8,
"parent_id" => 3,
"name" => "id8"
]
]
]
]
],
[
"id" => 4,
"name" => "id4",
"children" => [
[
"id" => 9,
"parent_id" => 4,
"name" => "id9",
"children" => [
[
"id" => 10,
"parent_id" => 9,
"name" => "id10"
]
]
]
]
],
[
"id" => 7,
"name" => "id7",
"children" => [
]
]
];
$array = flatTree($tree);
print_r($array);

Creating multidimensional array and removing empty categories

I have a single-dimensional array of categories; some of which are children of other categories, and some of which contain 'elements'. I need to turn it into a multidimensional array, and remove any categories which have no elements in them or any of their children (or children's children...).
I have the following array:
$category_array = array(
1 => array(
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
),
4 => array(
'elementcount' => 0,
'parentcat' => 1,
'depth' => 2
),
8 => array(
'elementcount' => 0,
'parentcat' => 4,
'depth' => 3
),
9 => array(
'elementcount' => 2,
'parentcat' => 4,
'depth' => 3
),
11 => array(
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
),
12 => array(
'elementcount' => 0,
'parentcat' => 11,
'depth' => 2
),
21 => array(
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
)
);
and I need the following array:
$multidimensional_array = array(
1 => array(
'elementcount' => 3,
'children' => array(
4 => array(
'elementcount' => 0,
'children' => array(
9 => array(
'elementcount' => 2
)
)
)
)
),
11 => array(
'elementcount' => 3,
),
21 => array(
'elementcount' => 3,
)
);
How can this be achieved?
This would be an approach:
<?php
$input = [
1 => [
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
],
4 => [
'elementcount' => 0,
'parentcat' => 1,
'depth' => 2
],
8 => [
'elementcount' => 0,
'parentcat' => 4,
'depth' => 3
],
9 => [
'elementcount' => 2,
'parentcat' => 4,
'depth' => 3
],
11 => [
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
],
12 => [
'elementcount' => 0,
'parentcat' => 11,
'depth' => 2
],
21 => [
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
]
];
$maxDepth = max(array_column($input, 'depth'));
// handle elements from higher to lower depth
for ($d = $maxDepth; $d >= 0; $d--) {
array_walk($input, function(&$entry, $index) use (&$input, $d) {
if (isset($entry['depth']) && $entry['depth'] == $d) {
// omit entries without elements or elements in children
if ($entry['elementcount'] < 1 && empty($entry['children'])) {
unset($input[$index]);
// handle as child entry of a parent entry
} else if (array_key_exists($entry['parentcat'], $input)) {
$input[$entry['parentcat']]['children'][$index] = [
'elementcount' => $entry['elementcount'],
'children' => isset($entry['children']) ? $entry['children'] : []
];
unset($input[$index]);
// handle as ordinary entry
} else {
$input[$index] = [
'elementcount' => $entry['elementcount'],
'children' => isset($entry['children']) ? $entry['children'] : []
];
}
}
});
}
print_r($input);
The strategy:
handle higher depths first so that the order of input elements does not matter
for each element check if the parent exists, if so stuff it in there
redefine all handled elements
The obvious output is:
(
[1] => Array
(
[elementcount] => 3
[children] => Array
(
[4] => Array
(
[elementcount] => 0
[children] => Array
(
[9] => Array
(
[elementcount] => 2
[children] => Array
(
)
)
)
)
)
)
[11] => Array
(
[elementcount] => 3
[children] => Array
(
)
)
[21] => Array
(
[elementcount] => 3
[children] => Array
(
)
)
)
I took the liberty to create a slightly modified result compared to your suggestion:
The 'children' property always exists as an array. That makes the usage of the result easier and more robust later on. I'd say that in general all elements inside a structure should have identical structure themselves if possible ...
function isParent($id, $list) : bool {
foreach($list as $item) {
if ($item['parentcat'] === $id) {
return true;
}
}
return false;
}
function buildLevel($parent, $catsByParent) : array {
$result = $catsByParent[$parent] ?? [];
foreach($result as $id => $cat) {
if (isset($catsByParent[$id])) {
$result[$id]['children'] = buildLevel($id, $catsByParent);
unset($catsByParent[$id]);
}
}
return $result;
}
// Filter out empty categories
$cats = array_filter(
$category_array,
function($cat, $id) use($category_array) {
return $cat['elementcount']>0 || isParent($id, $category_array);
},
ARRAY_FILTER_USE_BOTH
);
$catsByParent = [];
// Build cats list keyed by parentcat
foreach($cats as $id => $cat) {
$parent = $cat['parentcat'];
unset($cat['parentcat'], $cat['depth']);
$catsByParent[$parent] = ($catsByParent[$parent] ?? []) + [$id => $cat];
}
// Build result
$multidimensional_array = buildLevel(0, $catsByParent);
print_r($multidimensional_array);
First one filters out empty elements, i.e "categories which have no elements in them or any of their children". (children's children requirement sounds strange, wouldn't that be "any of their children" one level further down?)
Then the remaining categories are grouped/sorted by parentcat, aka "level id", to make the list workable :).
Then that list is traversed, starting with level id 0 at the top, and recursively processed(the children) as deep down as needed.

Print parent child category path in "/" format like "parentCatId/childCatId/subChildCatId/so on"

I want to print category path in "/" format like "parentCatId/childCatId/subChildCatId/so on".
I want to print all parent child categoryId in single path format.
$cats = array(
array("catId" => 1, "parentId" => 0),
array("catId" => 2, "parentId" => 1),
array("catId" => 3, "parentId" => 1),
array("catId" => 4, "parentId" => 2),
array("catId" => 5, "parentId" => 2),
array("catId" => 6, "parentId" => 3),
array("catId" => 7, "parentId" => 3),
array("catId" => 8, "parentId" => 4),
);
function getCatIdByParent($catArr, $catId, $catPath="/") {
$parentCatId = $catArr[$catId]['parentId'];
if($parentCatId == 0) {
$catPath .= $catId."/";
return $catPath;
} else {
$catPath .= $catId."/";
$a = getCatIdByParent($catArr, $parentCatId, $catPath);
}
return $a;
}
foreach($cats as $catInfo) {
$catArr[$catInfo['catId']] = $catInfo;
}
foreach($catArr as $catId => $catInfo) {
$catArr2[] = getCatIdByParent($catArr, $catId);
}
This gives out :
Array
(
[0] => /1/
[1] => /2/1/
[2] => /3/1/
[3] => /4/2/1/
[4] => /5/2/1/
[5] => /6/3/1/
[6] => /7/3/1/
[7] => /8/4/2/1/
)
But I want to bellow output:
Array
(
[0] => /5/2/1/
[1] => /6/3/1/
[2] => /7/3/1/
[3] => /8/4/2/1/
)
You will have to make the 'rootParents' (0,1,2,3) as seperate indexes, with their id being the key, you then can use the 'outter' array keys as the rootParentID's and you get the wanted result.
Example:
$catsParents[0] = array(
array("catId" => 1, "parentId" => 0),
array("catId" => 2, "parentId" => 1),
);
$catsParents[1] = array (etc
foreach($catParents as $parent){
$parentId = key($parent);
// do your magic
}

Remove element from array in php

I am new to php and i want to remove element from array Here is my array:
Array
(
[Total] => 21600000
[Items] => Array
(
[2-13] => Array
(
[Item] => 2
[PID] => 13
[UPrice] => 11000000
[Qty] => 1
[Total] => 11000000
)
[58-167] => Array
(
[Item] => 58
[PID] => 167
[UPrice] => 5300000
[Qty] => 1
[Total] => 5300000
)
)
)
And i want to remove array element by PID.
I have try this but no luck:-
$ShoppingBag =$_SESSION['ssss'];
if ($ShoppingBag !== null && $ShoppingBag['Total'] > 0) {
foreach ($ShoppingBag['Items'] as $IOrder) {
if($IOrder["PID"]==13)
{
unset($ShoppingBag[$IOrder]);
}else
{
}
}
}
Please help. Thanks
You can try with one simple array map :)
$arr = [
'Total' => 21600000,
'Items' => [
'2-13' => [
'Item' => 2,
'PID' => 13,
'UPrice' => 11000000,
'Qty' => 1,
'Total' => 11000000
],
'58-167'=> [
'Item' => 58,
'PID' => 167,
'UPrice' => 5300000,
'Qty' => 1,
'Total' => 5300000
]
]
];
$test = array_map(function($ar) {
foreach($ar as $k=>$i) {
if( isset($i['PID']) && $i['PID'] == '13')
unset($ar[$k]);
}
return $ar; } , $arr);
var_dump($test);
You need 2 loop to do the action you want.
foreach($my_array as $key=>$value)
{
if(is_array($value))
{
foreach($value as $k=>$v)
{
if($k == 'PID')
{
unset($value[$k]);
}
}
}
}
with this you can remove only element with key PID.
Hi youre unsetting the $IOrder instead of the Item that you want to delete:
This code is a solution an i tested it :
$ShoppingBag = Array
(
"Total" => 21600000,
"Items" => Array
(
"2-13" => Array
(
"Item" => 2,
"PID" => 13,
"UPrice" => 11000000,
"Qty" => 1,
"Total" => 11000000,
),
"58-167" => Array
(
"Item" => 58,
"PID" => 167,
"UPrice" => 5300000,
"Qty" => 1,
"Total" => 5300000,
),
),
);
foreach($ShoppingBag["Items"] as $key => $value){
if($value["PID"]==13){
unset($ShoppingBag["Items"][$key]);
}
}
You should know that always when you're using foreach loop the foreach( $a as $b ) when you do something to $b , $a remains the same because tey are different variables :)
Hope it will help you .
Regards.
$arr = [
'Total' => 21600000,
'Items' => [
'2-13' => [
'Item' => 2,
'PID' => 13,
'UPrice' => 11000000,
'Qty' => 1,
'Total' => 11000000
],
'58-167'=> [
'Item' => 58,
'PID' => 167,
'UPrice' => 5300000,
'Qty' => 1,
'Total' => 5300000
]
]
];
$pid_to_remove = 13;
$new_ar = array_filter(
$arr,
function ($v) using ($pid_to_remove) {
return (!isset($v['PID'])) || ($v['PID'] != $pid_to_remove);
}
);

How to get a tree-like array from another array?

With a table on my database that stores items of a menu, where every item has an ID, a NAME, and a FATHER ID, I need to arrange it and get a tree-like structure of multiple levels. What I need is an array with the top level menus, then every element with his 'childs' array that contains the sub menus, and this sub menus with their 'childs' array containing their respective sub sub menus an so for. English is no my native language so bear with me :)
An example for better understanding.
I have the following menu in array form:
1- System
2- Profile
3- Account
4- Info
5- Security
6- Logout
With the following array:
$array = array(
array('id' => 1, 'item'=>'System', 'id_father' => null),
array('id' => 2, 'item'=>'Profile', 'id_father' => 1),
array('id' => 3, 'item'=>'Account', 'id_father' => 2),
array('id' => 4, 'item'=>'Info', 'id_father' => 3),
array('id' => 5, 'item'=>'Security', 'id_father' => 3),
array('id' => 6, 'item'=>'Logout', 'id_father' => 1)
);
How can I get the following ? :
array(
array('id' => 1, 'item'=>'System', 'id_father' => null,
'childs' => array(
array('id' => 2, 'item'=>'Profile', 'id_father' => 1),
array('id' => 3, 'item'=>'Account', 'id_father' => 2,
'childs' => array(
array('id' => 4, 'item'=>'Info', 'id_father' => 3),
array('id' => 5, 'item'=>'Security', 'id_father' => 3)
),
),
),
),
array('id' => 6, 'item'=>'Logout', 'id_father' => 1)
);
Change $array to :
$array = array(
array('id' => 1, 'item'=>'System', 'id_father' => null),
array('id' => 2, 'item'=>'Profile', 'id_father' => 1),
array('id' => 3, 'item'=>'Account', 'id_father' => 1), // set id_father = 1
array('id' => 4, 'item'=>'Info', 'id_father' => 3),
array('id' => 5, 'item'=>'Security', 'id_father' => 3),
array('id' => 6, 'item'=>'Logout', 'id_father' => null) // edited to set id_father = null
);
Do it:
function tree( $ar, $pid = null ) {
$op = array();
foreach( $ar as $item ) {
if( $item['id_father'] == $pid ) {
$op[$item['id']] = array(
'item' => $item['item'],
'id_father' => $item['id_father'],
'id' => $item['id']
);
// using recursion
$children = tree( $ar, $item['id'] );
if( $children ) {
$op[$item['id']]['childs'] = $children;
}
}
}
return $op;
}
$tree = tree($array);
echo '<pre>';
print_r( $tree);
echo '</pre>';
// OUTPUT
Array
(
[1] => Array
(
[item] => System
[id_father] =>
[id] => 1
[childs] => Array
(
[2] => Array
(
[item] => Profile
[id_father] => 1
[id] => 2
)
[3] => Array
(
[item] => Account
[id_father] => 1
[id] => 3
[childs] => Array
(
[4] => Array
(
[item] => Info
[id_father] => 3
[id] => 4
)
[5] => Array
(
[item] => Security
[id_father] => 3
[id] => 5
)
)
)
)
)
[6] => Array
(
[item] => Logout
[id_father] =>
[id] => 6
)
)
There is no need to recursion in this way :
$pool = array();
foreach ($array as $value) {
$pool[$value['id']] = $value;
$pool[$value['id']]['children'] = array();
}
foreach ($pool as $k => $v) {
if ($v['id_father']) {
$pool[$v['id_father']]['children'][] = &$pool[$k];
}
else
$parent[] = $v['id'];
}
$result = array();
foreach ($parent as $val) {
$result = $result + $pool[$val];
}
print_r($result);

Categories