Build a Tree from a Flat Array in PHP

Build a tree from a flat array in PHP

You forgot the unset() in there bro.

function buildTree(array &$elements, $parentId = 0) {
$branch = array();

foreach ($elements as $element) {
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[$element['id']] = $element;
unset($elements[$element['id']]);
}
}
return $branch;
}

How to build grouped tree array from flat array in php

function buildTree(array $flat)
{
$grouped = [];
foreach ($flat as $node) {
$grouped[$node['parent_id']][] = $node;
}

$fnBuilder = function ($siblings) use (&$fnBuilder, $grouped) {
foreach ($siblings as $k => $sibling) {
$id = $sibling['id'];
if (isset($grouped[$id])) {
$sibling['children'] = $fnBuilder($grouped[$id]);
}
$siblings[$k] = $sibling;
}
return $siblings;
};
return $fnBuilder($grouped[0]);
}


$tree = buildTree($flat);

pass tree structure array to groupedTree();

$groupd = groupedTree($tree);

echo json_encode($groupd, JSON_PRETTY_PRINT);


function groupedTree($tree)
{
$groupedByFuncTableName = array_reduce($tree, function (array $accumulator, array $element) {
if (isset($element['children'])) {
$element['children'] = groupedTree($element['children']);
}
$accumulator[$element['name']][] = $element;

return $accumulator;
}, []);
return $groupedByFuncTableName;
}

create array tree from array list

oke this is how i solved it:

$arr = array(
array('id'=>100, 'parentid'=>0, 'name'=>'a'),
array('id'=>101, 'parentid'=>100, 'name'=>'a'),
array('id'=>102, 'parentid'=>101, 'name'=>'a'),
array('id'=>103, 'parentid'=>101, 'name'=>'a'),
);

$new = array();
foreach ($arr as $a){
$new[$a['parentid']][] = $a;
}
$tree = createTree($new, array($arr[0]));
print_r($tree);

function createTree(&$list, $parent){
$tree = array();
foreach ($parent as $k=>$l){
if(isset($list[$l['id']])){
$l['children'] = createTree($list, $list[$l['id']]);
}
$tree[] = $l;
}
return $tree;
}

Build directory tree from flat array of paths in PHP

You could do this in two steps:

  • Create a hierarchy of associative arrays, where the labels are the keys, and nested arrays correspond to children.
  • Transform that structure to the target structure

Code:

function buildTree($branches) {
// Create a hierchy where keys are the labels
$rootChildren = [];
foreach($branches as $branch) {
$children =& $rootChildren;
foreach($branch as $label) {
if (!isset($children[$label])) $children[$label] = [];
$children =& $children[$label];
}
}
// Create target structure from that hierarchy
function recur($children) {
$result = [];
foreach($children as $label => $grandchildren) {
$node = ["label" => $label];
if (count($grandchildren)) $node["children"] = recur($grandchildren);
$result[] = $node;
}
return $result;
}
return recur($rootChildren);
}

Call it likes this:

$tree = buildTree($branches);

The above will omit the children key when there are no children. If you need to have a children key in those cases as well, then just remove the if (count($grandchildren)) condition, and simplify to the following version:

function buildTree($branches) {
// Create a hierchy where keys are the labels
$rootChildren = [];
foreach($branches as $branch) {
$children =& $rootChildren;
foreach($branch as $label) {
if (!isset($children[$label])) $children[$label] = [];
$children =& $children[$label];
}
}
// Create target structure from that hierarchy
function recur($children) {
$result = [];
foreach($children as $label => $grandchildren) {
$result[] = ["label" => $label, "children" => recur($grandchildren)];
}
return $result;
}
return recur($rootChildren);
}

How to make a tree from a flat array - php

I don't know if this is the 'correct' way to do this, but if you want to make a recursive structure, then the easy way is to use a recursive function:

$root = array('name'=>'/', 'children' => array(), 'href'=>'');

function store_file($filename, &$parent){

if(empty($filename)) return;

$matches = array();

if(preg_match('|^([^/]+)/(.*)$|', $filename, $matches)){

$nextdir = $matches[1];

if(!isset($parent['children'][$nextdir])){
$parent['children'][$nextdir] = array('name' => $nextdir,
'children' => array(),
'href' => $parent['href'] . '/' . $nextdir);
}

store_file($matches[2], $parent['children'][$nextdir]);
} else {
$parent['children'][$filename] = array('name' => $filename,
'size' => '...',
'href' => $parent['href'] . '/' . $filename);
}
}

foreach($files as $file){
store_file($file, $root);
}

Now, every element of root['children'] is an associative array that hash either information about a file or its own children array.

PHP - Making a nested tree menu structure from a flat array

One way to solve this to make use of variable aliases. If you take care to manage a lookup-table (array) for the IDs you can make use of it to insert into the right place of the hierarchical menu array as different variables (here array entries in the lookup table) can reference the same value.

In the following example this is demonstrated. It also solves the second problem (implicit in your question) that the flat array is not sorted (the order is undefined in a database result table), therefore a submenu entry can be in the resultset before the menu entry the submenu entry belongs to.

For the example I created a simple flat array:

# some example rows as the flat array
$rows = [
['id' => 3, 'parent_id' => 2, 'name' => 'Subcategory A'],
['id' => 1, 'parent_id' => null, 'name' => 'Home'],
['id' => 2, 'parent_id' => null, 'name' => 'Categories'],
['id' => 4, 'parent_id' => 2, 'name' => 'Subcategory B'],
];

Then for the work to do there are tow main variables: First the $menu which is the hierarchical array to create and second $byId which is the lookup table:

# initialize the menu structure
$menu = []; # the menu structure
$byId = []; # menu ID-table (temporary)

The lookup table is only necessary as long as the menu is built, it will be thrown away afterwards.

The next big step is to create the $menu by traversing over the flat array. This is a bigger foreach loop:

# build the menu (hierarchy) from flat $rows traversable
foreach ($rows as $row) {
# map row to local ID variables
$id = $row['id'];
$parentId = $row['parent_id'];

# build the entry
$entry = $row;
# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]

# register the entry in the menu structure
if (null === $parentId) {
# special case that an entry has no parent
$menu[] = &$entry;
} else {
# second special case that an entry has a parent
$byId[$parentId]['submenus'][] = &$entry;
}

# register the entry as well in the menu ID-table
$byId[$id] = &$entry;

# unset foreach (loop) entry alias
unset($entry);
}

This is where the entries are mapped from the flat array ($rows) into the hierarchical $menu array. No recursion is required thanks to the stack and lookup-table $byId.

The key point here is to use variable aliases (references) when adding new entries to the $menu structure as well as when adding them to $byId. This allows to access the same value in memory with two different variable names:

        # special case that an entry has no parent
$menu[] = &$entry;
...

# register the entry as well in the menu ID-table
$byId[$id] = &$entry;

It is done with the = & assignment and it means that $byId[$id] gives access to $menu[<< new key >>].

The same is done in case it is added to a submenu:

    # second special case that an entry has a parent
$byId[$parentId]['submenus'][] = &$entry;
...

# register the entry as well in the menu ID-table
$byId[$id] = &$entry;

Here $byId[$id] points to $menu...[ << parent id entry in the array >>]['submenus'][ << new key >> ].

This is solves the problem to always find the right place where to insert a new entry into the hierarchical structure.

To deal with the cases that a submenu comes in the flat array before the menu entry it belongs to, the submenu when initialized for new entries needs to be taken out of the lookup table (at [1]):

# init submenus for the entry
$entry['submenus'] = &$byId[$id]['submenus']; # [1]

This is a bit of a special case. In case that $byId[$id]['submenus'] is not yet set (e.g. in the first loop), it is implicitly set to null because of the reference (the & in front of &$byId[$id]['submenus']). In case it is set, the existing submenu from a not yet existing entry will be used to initialize the submenu of the entry.

Doing so is enough to not depend on any specific order in $rows.

This is what the loop does.

The rest is cleanup work:

# unset ID aliases
unset($byId);

It unsets the look ID table as it is not needed any longer. That is, all aliases are unset.

To complete the example:

# visualize the menu structure
print_r($menu);

Which then gives the following output:

Array
(
[0] => Array
(
[id] => 1
[parent_id] =>
[name] => Home
[submenus] =>
)

[1] => Array
(
[id] => 2
[parent_id] =>
[name] => Categories
[submenus] => Array
(
[0] => Array
(
[id] => 3
[parent_id] => 2
[name] => Subcategory A
[submenus] =>
)

[1] => Array
(
[id] => 4
[parent_id] => 2
[name] => Subcategory B
[submenus] =>
)

)

)

)

I hope this is understandable and you're able to apply this on your concrete scenario. You can wrap this in a function of it's own (which I would suggest), I only kept it verbose for the example to better demonstrate the parts.

Related Q&A material:

  • Php: Converting a flat array into a tree like structure
  • Convert a series of parent-child relationships into a hierarchical tree?
  • Build a tree from a flat array in PHP

PHP build breadcrumb of tree array / flat array

You could optimise this by indexing flattened array - too many search loops now (post your flattening function if you want better solution). The function you have doesn't need big changes though. You should build your parent structure first, push current position and then return it to higher scope (as its parent structure):

function build_breadcrumb_from_category($categories, $pid)
{
$result = [];
foreach ($categories as $category) {
if ($pid == $category['id']) {
if ($category['parent_id'] > 0) {
# Page has parents
$result = build_breadcrumb_from_category($categories, $category['parent_id']);
}

$result[] = [$category['name'], $category['permalink']];
}
}
return $result;
}

Here's the solution with indexed category list - first function flattens the tree, second one uses its id indexes to build breadcrub path:

function category_index($category_tree)
{
$result = [];
foreach($category_tree as $category) {
if (!empty($category['children'])) {
$result = $result + category_index($category['children']);
}
unset($category['children']);
$result[$category['id']] = $category;
}
return $result;
}

function category_breadcrumb($category_index, $pid)
{
if (empty($category_index[$pid])) { return []; }

$category = $category_index[$pid];
$result = ($category['parent_id']) ? category_breadcrumb($category_index, $category['parent_id']) : [];
$result[] = [$category['name'], $category['permalink']];

return $result;
}


Related Topics



Leave a reply



Submit