2

I'm looping trough the below array that has multiple entries. In the example I have 3 elements where each element have 3 values inside.

[0]
   ['name'] => 'aaa'
   ['id'] => 38679
   ['parent-id'] => 0
[1]
   ['name'] => 'bbb'
   ['id'] => 38830
   ['parent-id'] => 38679
[2]
   ['name'] => 'ccc'
   ['id'] => 38680
   ['parent-id'] => 38830

So, looping trough the array, I need to construct another one in this format (the idea is to construct the hierarchy):

[0]
   [38679] => 'aaa'
          [38830] => 'bbb'
                 [38680] => 'ccc'

Maybe there's another way to do this. Any suggestion would be great.

2
  • It's not hierarchy and it's not clear what you want to get. What's the logic behind the array you want? The hierachy based on id and parent-id elements would be something like aaa -> bbb -> ccc where bbb is a child of aaa and so on. Is this what you want? Commented Nov 1, 2015 at 14:42
  • You're right Timofey, I had the wrong array. I edited the question. Commented Nov 1, 2015 at 14:48

2 Answers 2

2

One of possible solutions is to use recursive function:

$test = [
    0 => [
        'name' => 'aaa',
        'id' => 38679,
        'parent-id' => 0
    ],
    1 => [
        'name' => 'bbb',
        'id' => 38830,
        'parent-id' => 38679
    ],
    2 => [
        'name' => 'ccc',
        'id' => 38680,
        'parent-id' => 38830
    ]
];

function make_hierarchy(array $arr, $parent = 0) {
    $result = array();

    foreach($arr as $item) {
        if ($item['parent-id'] == $parent) {
            $children = make_hierarchy($arr, $item['id']);
            $child = $item;
            if ($children) {
                $child['children'] = $children;
            }
            $result[] = $child;
        }
    }
    return $result;
}

$r = make_hierarchy($test);

var_dump($r);
Sign up to request clarification or add additional context in comments.

5 Comments

$result[] = $child; gives an error -- $child is not declared.
The code now runs, but the output does not have any id properties, nor do the indices of the arrays correspond to id values. And somehow the node for 'ccc' is lost. The result only has two nodes with a name property ( 'aaa' and 'bbb').
Alright, edited the answer. It's not a big deal to add specific conent whereas you have the algorithm.
Any fix for having the 'ccc' node in the output? You should change also to: $result[$item['id']] = $child;
Thanks, I somehow missed the issue.
1

Iterative solution:

// set up test data
$arr = [
    0 => [
        'name' => 'aaa',
        'id' => 38679,
        'parent-id' => 0
        ],
    1 => [
        'name' => 'bbb',
        'id' => 38830,
        'parent-id' => 38679
        ],
    2 => [
        'name' => 'ccc',
        'id' => 38680,
        'parent-id' => 38830
        ]
    ];

// hierarchy array that will have the result
$hier = [];

// Start off by putting all nodes as top-level nodes in the hierarchy
foreach ($arr as $node) {
    $node['children'] = [];
    $hier[$node['id']] = $node;
};

// Iterate to move nodes from the top-level under their respective parents.
do {
    $count = count($hier);
    echo 'Next iteration: ', $count, ' top-level nodes left<br>';

    // Collect the ids of parents of top-level nodes 
    $parents = [];
    foreach ($hier as $node) {
        $parents[$node['parent-id']] = 1;
    }

    // Find all nodes that are candidate to be moved     
    foreach ($hier as $node) {
        // Treat node only if it has gathered all of its children
        if (!array_key_exists($node['id'], $parents)) {
            $parentId = $node['parent-id'];
            // Remove the parent-id attribute, it is not needed anymore
            unset($node['parent-id']);
            // Remove the node from the top-level
            unset($hier[$node['id']]);
            // Check that referenced parent exists, parent-id=0 will fail this.
            if (array_key_exists($parentId, $hier)) {
                // Move this node under its parent node, keyed by its ID
                echo 'Node ', $node['id'], ' moved under ', $parentId, '<br>';
                $hier[$parentId]['children'][$node['id']] = $node;
            } else {
                // Node is stays on top-level (but without parent-id property):
                echo 'Node ', $node['id'], ' stays at top level<br>';
                $hier[] = $node;
            }
        }
    };
    // keep going as long as we were able to move at least one node 
} while (count($hier) < $count);

echo 'Done. <pre>';    
print_r($hier);
echo '</pre>';    

Tested, with output:

Next iteration: 3 top-level nodes left
Node 38680 moved under 38830
Next iteration: 2 top-level nodes left
Node 38830 moved under 38679
Next iteration: 1 top-level nodes left
Node 38679 stays at top level
Done.

Array
(
    [38831] => Array
        (
            [name] => aaa
            [id] => 38679
            [children] => Array
                (
                    [38830] => Array
                        (
                            [name] => bbb
                            [id] => 38830
                            [children] => Array
                                (
                                    [38680] => Array
                                        (
                                            [name] => ccc
                                            [id] => 38680
                                            [children] => Array
                                                (
                                                )
                                        )
                                )
                        )
                )
        )
)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.