views:

89

answers:

2

Hello. I have an array like this:

Array ( 
[0] => Array ( [id] => 1000 [enroller_id] => 1005) 

[1] => Array ( [id] => 1005 [enroller_id] =>) 

[2] => Array ( [id] => 1101 [enroller_id] => 1000 ) 

[3] => Array ( [id] => 1111 [enroller_id] => 1000 ) 
)

I want to create hierarchy array like this:

Array(
[1005] => Array(
               [1000] => Array(
                              [1101] => ...
                              [1111] => ...
                              )
               )
)

Can you help me? I think that it is a recursion.

+3  A: 
//$paths is an array of references, in which _every_ item will sit at 'root'
//level, but also as a reference as a child to it's parent.

//initialize location of parentless / root items:
$paths = array('N'=>array());

foreach($items as $item){
    //$target is the parent-id, or 'N' if we are a root node
    $target = isset($item['enroller_id']) && !empty($item['enroller_id']) ? $item['enroller_id'] :'N';

    //if the parent is not yet in the paths array, make an entry for it
    if(!isset($paths[$target]))      $paths[$target] = array();

    //if this item is not yet in the array (the previous statement could  
    //already have inserted it, make an array(
    if(!isset($paths[$item['id']]))  $paths[$item['id']] = array();

    //add the current item as a reference to it's parent
    $paths[$target][$item['id']] =  &$paths[$item['id']];

    //Setting it as a reference has this consequence:
    //   when adding an item to the $paths[$id] array, it will 
    //   automatically be added to $paths[$parent][$id], as 
    //   both $paths[$id] & $paths[$parent][$id] point to the same
    //   location in memory.
    //   This goes to infinite depth: if $foo is a child of $id, and you
    //   add a node to it, it will be in
    //   $paths[$foo]               = array($child);
    //   $paths[$id][[$foo]         = array($child);
    //   $paths[$parent][$id][$foo] = array($child);
    //
    //   Altering an item at any location in paths / the tree will alter it anywhere
    //   in the paths / tree, unsetting it anywhere only unset the data at that location, 
    //   other locations will still have the same data (and the data will keep 
    //   existing until the last reference is unset())

}
//we are only interested in the 'root' nodes (all other nodes should be subnodes
//in this tree
$tree = $paths['N'];
//remove all unused references in the $paths array
//do remember to do this: cleaning up references is important
unset($paths);
//tree is now an array of 'normal' values (i.e. only 1 reference to each datapoint exists
var_dump($tree);

Don't forget to unset paths: references can really bite you with difficult to trace errors if you don't take proper care.

Wrikken
Can you explain how this works?
Sjoerd
It's just linking current node and its parent node on every iteration. At the start, you just have two orphaned nodes. Eventually they get linked together until you've got the final tree. The only side effect is that unrooted nodes will be silently ignored, but that's not really a fault of the algorithm.
konforce
It is not very good solution.
Alexander.Plutov
Added comments to the solution for an explanation. @Alexander.Plutov : care to tell me why? No data-duplication, only one loop, pretty efficient, and no references lying around at the end. As an extra bonus, **order of items does not matter**: you can have a child in the input long before the parent, and it **will** still work.
Wrikken
It's not that clear cut. Although this solution is quite hard to understand, it only loops through the array once. This makes it much faster than the recursive solution if you have a big tree.
Sjoerd
The best way to have a fast application is to use proper algorithms. This was the first solution that came to my mind. Seems almost trivial...
konforce
Wrikken
+2  A: 

This will do what you want, except it does not put the first element (1005) in the array:

function create_array($number, $data)
{
    $result = array();
    foreach ($data as $row)
    {
        if ($row['enroller_id'] == $number)
        {
            $result[$row['id']] = create_array($row['id'], $data);
        }
    }
    return $result;
}

print_r(create_array(1005, $data));

Output:

Array
(
    [1000] => Array
        (
            [1101] => Array ()
            [1111] => Array ()
        )
)
Sjoerd
Thanks. It is ideal solution!
Alexander.Plutov
How do we magically know 1005? And shouldn't it be in the result?
Wrikken
1005 should be replaced with null, and then it would work. And as often is the case, the recursion is simple but causes excessive iterations.
konforce