i have a one-dimensional array with stdclasses inside, these objects have a name and a "abh" attribute. now i want to get a tree-view like multidimensional array of that. Lets say my array looks like this:

$ar = array(
    (object)array("name" => "Test1","otherstuff"=>array(),"abh"=>""),
    (object)array("name" => "Test2","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test3","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test4","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test5","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test6","otherstuff"=>array(),"abh"=>"Test5"),
    (object)array("name" => "Test7","otherstuff"=>array(),"abh"=>"Test5"),
    (object)array("name" => "Test8","otherstuff"=>array(),"abh"=>array("Test5","Test7")),
    (object)array("name" => "Test9","otherstuff"=>array(),"abh"=>"Test8"),
    (object)array("name" => "Test10","otherstuff"=>array(),"abh"=>"Test6"),
    (object)array("name" => "Test11","otherstuff"=>array(),"abh"=>"Test9"),
);

the data cames from functions called before, not from database. The Result is something like this:

Array
(
[0] => stdClass Object
    (
        [name] => Test1
        [otherstuff] => Array
            (
            )

        [abh] => 
    )

[1] => stdClass Object
    (
        [name] => Test2
        [otherstuff] => Array
            (
            )

        [abh] => Test1
    )
[...]
)

so now, i want this array will become sorted by the "abh" attribute. the first one where the "abh" is empty, its the root, all others will become childs of it. so the function has to add a "childs" attribute to the main IF it has childs. But now this one is type of special because the "abh" can be a array itsself and reasign to n-parents. In my example above, the Test8 should be a child of Test5 and Test7 (clone).

I've tried to handle this with a recursive function and loops in a foreach. In there it checks the abh of a element againts parents thats exists, but dont work. array_filter() doenst do that like i want.

so, i want this result:

Array
(
    [Test1] => stdClass Object
        (
            [name] => Test1
            [otherstuff] => Array()
            [abh] => 
            [childs] => Array
                (
                    [Test2] => stdClass Object
                        [...] and so on
                )
        )
)

any ideas about that? i also dont know how much "levels" the returned array will have, thats on how much input objects and "abh"-assignes exists. The seconds is that the "abh" array can have up to 10 "parents".

Thanks for any help!


as requestet my non-working prototyp for that "sorter"

function checkABH(&$return,&$ar,$deph) {

    // clone all with array-abh
    $count = count($ar);
    for($x=0;$x<$count;$x++) {#$ar as$key=>$val) {
        if(is_array($ar[$x]->abh)) {
            $clone = $ar[$x];
            foreach($ar[$x]->abh as$abh) {
                #echo "<br>$x @@ $abh";
                $clone->abh = $abh;
                $ar[]       = $clone;
                #echo '<pre>'.print_r($clone,true).'</pre>';
            }
            // delete array-abh-element
            unset($ar[$x]);
        }
    }

    echo '<pre>'.print_r($ar,true).'</pre>';
    echo '</div>';
    echo '<div style="float:left;width:auto;margin:0px 10px;"><h3>Result:</h3>';

    // pass to sorter
    checkABH_a($return,$ar,$deph);
}

function checkABH_a(&$return,&$ar,$deph) {

    $test_abhs = array();

    foreach($ar as$key=>$val) {

        $val->childs = array();

        if(isset($return[$deph])&&isset($return[$deph]->name)&&$val->abh==$return[$deph]->name) {
            $return[$deph]->childs[] = $val;
            unset($ar[$key]);
        } elseif($val->abh==$deph) {
            $return[$val->abh] = $val;
            unset($ar[$key]);
        } else {
            $test_abhs[] = $val->abh;
        }
    }

    if(count($test_abhs)>0) {
        $test_abhs = array_unique($test_abhs);
        #echo '<pre>'.print_r($test_abhs,true).'</pre>';
        foreach($test_abhs as$abh) {
            checkABH_a($return,$ar,$abh);
        }
    }
}

echo '<div style="float:left;width:260px;border-right:1px solid #cccccc;margin:0px 10px;"><h3>Input</h3>';
echo '<pre>'.print_r($ar,true).'</pre>';
echo '</div>';
echo '<div style="float:left;width:260px;border-right:1px solid #cccccc;margin:0px 10px;"><h3>Cloned:</h3>';
$return = array();
checkABH($return,$ar,"");
echo'<pre>'.print_r($return,true).'</pre>';
echo '</div>';
  • And you have tried... what?? How to ask a good question – RiggsFolly Dec 8 '14 at 15:15
  • like i said above, a recursive function that pass the array to itself until its empty - in it it checks if a parent exists and put that child on it - but my solution wont work – UnskilledFreak Dec 8 '14 at 15:17
  • Well show your solution, then we can look at it and suggest improvments/corrections – RiggsFolly Dec 8 '14 at 15:19
  • i've added it - but it procedures an nearly endless loop or a fatal error caused by max_memory – UnskilledFreak Dec 8 '14 at 15:29
  • Is the object cast not optional for your case? Also, why the Test8 is a child of Test5 and Test7? – Goombi Dec 8 '14 at 15:40
up vote 1 down vote accepted

These recursive trees are not getting any easier, what with the 'multiple parent' requirement being added ;-/ Whatever, it still look interesting...

I am not going to document all the code here as I think i have commented it enough already.

Notes:

  • converted (duplicate) the 'multiple parents' to individual entries so the 'insertNode' function was easier to understand.
  • rather than use 'stdClass' i made a class called 'TreeNode' as i may want to add methods later.

Working version of the code at Viper-7.com using PHP 5.3.18

Any queries about the code then post comments and i will try an answer 'em.

<?php // https://stackoverflow.com/questions/27360813/php-convert-array-to-multidimensional-array-by-parent-id

$ar = array(
    (object) array("name" => "Test1", "otherstuff"=>array('parent is Test1'), "abh"=>""),
    (object) array("name" => "Test2", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test3", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test4", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test5", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test6", "otherstuff"=>array('parent is Test5'), "abh"=>"Test5"),
    (object) array("name" => "Test7", "otherstuff"=>array('parent is Test5'), "abh"=>"Test5"),
    (object) array("name" => "Test8", "otherstuff"=>array('parent is Test5 AND Test7'), "abh"=>array("Test5", "Test7")),
    (object) array("name" => "Test9", "otherstuff"=>array('parent is Test8'), "abh"=>"Test8"),
    (object) array("name" => "Test10", "otherstuff"=>array('parent is Test6'), "abh"=>"Test6"),
    (object) array("name" => "Test11", "otherstuff"=>array('parent is Test9'), "abh"=>"Test9"),
);


/*
 * The requirement is that for any child then the parent must be in the tree already.
 */

// Convert those parent with 'arrays of parents' into individual entries as it will make the insert
// easier to understand.

// i will also convert all the input into a 'TreeNode' class while i am doing this pass down the input. 
// i can add some methods later if i wish.

// i will also get the root of the tree while i am doing this...
$rootNode = new TreeNode(array_shift($ar));

$treeNodeList = array();
foreach($ar as $node) {
    if (is_array($node->abh)) { // generate duplicate nodes
        foreach($node->abh as $parentName) {
            $node->abh = $parentName; // make it an ordinary node
            $treeNodeList[] = new TreeNode($node);
        }
        continue;
    }
    $treeNodeList[] = new TreeNode($node);
}

// var_dump($ar, $rootNode, $treeNodeList);

// Ok, we now have a node list in the appropriate order - let us build the tree

/**
 * This is a 'multiway' tree .i.e. there can be any number of 'child' nodes
 *                                 for any parent
 * Usual rules:
 *   o The parent MUST be in the tree already.
 *   o Children are added, by searching from the root of the tree, in this version
 *
 */

// the output will be here
$theTree = array($rootNode->name => $rootNode);

foreach ($treeNodeList as $childNode) { // add it
    $inserted = insertNode($rootNode, $childNode);
    if (!$inserted) {
      var_dump('Unable to insert:', $childNode, ' in tree:', $rootNode);
      die('i am here: '. __FILE__.__LINE__);
    }
}

// show the tree
echo '<pre>';
   print_r($theTree);
echo '</pre>';

exit;
// --------- end of processing -------------

/**
 * Insert the node in the tree
 *
 * @param TreeNode $parent
 * @param TreeNode $newChild
 */
function insertNode(TreeNode &$parent, TreeNode $newChild) {
    if ($newChild->abh === $parent->name) { // add it to the current parent
        $parent->children[$newChild->name] = $newChild; // add child
        return true;
    }

    // check the children
    foreach($parent->children as $newParent) {
       if (insertNode($newParent, $newChild)) {
          return true;
       }
    }
    return false; // unable to insert child in the tree
}

// -----------------------------------------------------------------------
/**
 * treeNode: (Data and Relationships)
 *   nodeId:            'name'
 *   nodeData:          'otherstuff'
 *   nodeParentList:    'abh' - may be a list of parents.
 *                          Note: In this version of the code: All the parents
 *                                MUST be in the tree already.
 *   children:          array of treeNode - recursive structure
 */

// i will make the properties all public to make it easier to debug.

class TreeNode  {
    public $name = '';
    public $otherstuff = array();
    public $abh = '';
    public $children = array(); // list of child nodes

    public function __construct(stdClass $node)
    {
        $this->name = $node->name;
        $this->otherstuff = $node->otherstuff;
        $this->abh = $node->abh;
    }
} // class end --------------------------
  • Thank you so much! excactly what i wanted – UnskilledFreak Dec 9 '14 at 13:34

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.