Reputation: 137
I've been stuck in this since yesterday any help will be much appreciated. The situation is this: I have a table for navigation links with the following structure, id | parent_id | text
In my scenario the parent_id may be null(no sub-menu) or any id pointing back to the table which will make the record a sub-menu of the parent. I've tried many different ways to execute this but with no luck, the closest scenario I came up with is the following,
// All navigation link results are stored in $data
public static function transform(array &$data) {
$result = [];
$search = function(&$array = [], $parent = null) use (&$search, &$result){
foreach($array as $key => $value) {
/** If the element has an ancestor */
if($parent && $parent->id == $value->parent_id) {
$next = $parent->children[] = $value;
$search($array, $next);
unset($array[$key]);
}
}
return $parent;
};
foreach($data as $val) {
$result[] = $search($data, $val);
}
return $result;
}
The function works fine and tracks all the ancestors of a certain link but in the next iteration is tracking all the ancestors of the next element(even if it was a child of the previous one) so it comes back with a lot of duplicated data. I came up with the solution to unset the iterated element from the array but for some reason, the element is still being used in the next iteration. Here is my current data
Upvotes: 3
Views: 2240
Reputation: 6531
A simple concept for faster search and to process one item only once.
key as id
and values as array of child ids
. Will use this array to process next direct childs. It would be a depth first traversal.id
as key in input array, so we can directly access whole node when required.function generateTree($data){
$arrChild = []; // Store parent as key and its childs as array to quickly find out.
foreach($data as $obj){
$arrChild[$obj->parent_id][] = $obj->id;
$data[$obj->id] = $obj;
}
$final = [];
$setChild = function(&$array, $parents) use (&$setChild, $data, $arrChild){
foreach($parents as $parent){
$temp = $data[$parent];
// If key is set, that means given node has direct childs, so process them too.
if(isset($arrChild[$parent])){
$temp->children = [];
$setChild($temp->children, $arrChild[$parent]);
}
$array[] = $temp;
}
};
// Empty key would represent nodes with parent as `null`
$setChild($final, $arrChild['']);
return $final;
}
$final = generateTree($arr);
echo json_encode($final, JSON_PRETTY_PRINT);
Output:
[
{
"id": 1,
"navigation_id": 4,
"parent_id": null,
"text": "link 1",
"icon": "",
"url": null,
"page_id": 4,
"children": [
{
"id": 2,
"navigation_id": 4,
"parent_id": 1,
"text": "link 2",
"icon": "",
"url": null,
"page_id": 4,
"children": [
{
"id": 3,
"navigation_id": 4,
"parent_id": 2,
"text": "link 3",
"icon": "fas fa-ad",
"url": "https:\/\/google.com",
"page_id": null
},
{
"id": 4,
"navigation_id": 4,
"parent_id": 2,
"text": "link 4",
"icon": "fab fa-google",
"url": "https:\/\/google.com",
"page_id": null
}
]
}
]
},
{
"id": 5,
"navigation_id": 4,
"parent_id": null,
"text": "link 5",
"icon": "",
"url": null,
"page_id": 5,
"children": [
{
"id": 6,
"navigation_id": 4,
"parent_id": 5,
"text": "link 6",
"icon": "",
"url": null,
"page_id": 4
},
{
"id": 7,
"navigation_id": 4,
"parent_id": 5,
"text": "link 7",
"icon": "",
"url": null,
"page_id": 4
}
]
}
]
Upvotes: 3