Search code examples
phparraysmultidimensional-arrayparent-childhierarchical-data

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.


Solution

  • 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);