zephirus
zephirus

Reputation: 391

Transform simple array in hierarchy array

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.

Upvotes: 2

Views: 333

Answers (2)

trincot
trincot

Reputation: 350270

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
                                                (
                                                )
                                        )
                                )
                        )
                )
        )
)

Upvotes: 1

Timofey
Timofey

Reputation: 829

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

Upvotes: 2

Related Questions