views:

46

answers:

1

Based on this question: http://stackoverflow.com/questions/1310649/getting-a-modified-preorder-tree-traversal-model-nested-set-into-a-ul

The logic bellow is used to build an ordened list, but how to do the same with an array?

I want to build a nested array.

// bootstrap loop
$result = '';
$currDepth = -1;  // -1 to get the outer <ul>
while (!empty($tree)) {
  $currNode = array_shift($tree);
  // Level down?
  if ($currNode['depth'] > $currDepth) {
    // Yes, open <ul>
    $result .= '<ul>';
  }
  // Level up?
  if ($currNode['depth'] < $currDepth) {
    // Yes, close n open <ul>
    $result .= str_repeat('</ul>', $currDepth - $currNode['depth']);
  }
  // Always add node
  $result .= '<li>' . $currNode['title'] . '</li>';
  // Adjust current depth
  $currDepth = $currNode['depth'];
  // Are we finished?
  if (empty($tree)) {
    // Yes, close n open <ul>
    $result .= str_repeat('</ul>', $currDepth + 1);
  }
}

print $result;
+1  A: 

Ah, finally something for which references are handy:

<?php
$tree = array(
    array('Cat 1', 'depth' => 0),
    array('Cat 2', 'depth' => 1),
    array('Cat 3', 'depth' => 1),
    array('Cat 4', 'depth' => 2),
    array('Cat 5', 'depth' => 1),
    array('Cat 6', 'depth' => 2),
    array('Cat 7', 'depth' => 3),
    array('Cat 8', 'depth' => 1)
);
//same as before
$currDepth = -1;

//initilialize result
$result = array();

//create path structure for depths
$path = array();

//create 'root' node
$olditem = array('children'=> &$result);


foreach($tree as $item){
    if($item['depth'] > $currDepth){
        //remove possible old reference (old depth of other branch
        if(isset($path[$item['depth']])) unset($path[$item['depth']]);

        //make sure we have an array entry
        if(!isset($olditem['children'])) $olditem['children'] = array();

        //acquire target
        $path[$item['depth']] = &$olditem['children'];
    }
    if($item['depth'] != $currDepth) unset($olditem);
    //set correct target
    $currDepth = $item['depth'];
    //add item
    $path[$currDepth][] = &$item;
    //copy & remove reference
    $olditem = &$item;
    unset($item);
}
//always nice to clean up reference bombs:
unset($path);
unset($olditem);

var_dump($result);
?>
Wrikken
Thanks a lot! I've tested and works fine, now I'm trying to understand. Does the first unset on the path variable make useless its declaration outside the loop? $path = array(0 => array('children'=> AND if(isset($path[$item['depth']])) unset($path[$item['depth']]); At the first loop it will be deleted.
Keyne
Hmm, you're right, that one can go, it was superseded by the `$olditem` I edited in later. I'll edit the answer just the slightest in a moment.
Wrikken
When you unset $olditem why this doesn't affect $path[$item['depth']] as it's passed by reference? I'm confunse on that part. Seems like this doesn't affect the final result, in other words, if I change, the reference value is affected, but not if I unset.
Keyne
Hmm, step-by-step reference explaining is quite some work, I'll see what I can do later this evening.
Wrikken
Either way, thanks!
Keyne