recursion函数从数据库结果生成multidimensional array
我正在寻找写一个函数,它需要一个页面/类别的数组(从平面数据库结果),并生成一个基于父标识的嵌套页面/类别项目的数组。 我想recursion地做到这一点,以便可以完成任何级别的嵌套。
例如:我在一个查询中获取所有页面,这就是数据库表的样子
+-------+---------------+---------------------------+ | id | parent_id | title | +-------+---------------+---------------------------+ | 1 | 0 | Parent Page | | 2 | 1 | Sub Page | | 3 | 2 | Sub Sub Page | | 4 | 0 | Another Parent Page | +-------+---------------+---------------------------+
这是我想最终在我的视图文件中处理的数组:
Array ( [0] => Array ( [id] => 1 [parent_id] => 0 [title] => Parent Page [children] => Array ( [0] => Array ( [id] => 2 [parent_id] => 1 [title] => Sub Page [children] => Array ( [0] => Array ( [id] => 3 [parent_id] => 1 [title] => Sub Sub Page ) ) ) ) ) [1] => Array ( [id] => 4 [parent_id] => 0 [title] => Another Parent Page ) )
我已经看过并尝试过几乎所有我遇到的解决scheme(Stack Overflow中有很多这样的解决scheme,但是没有足够的通用性来支持页面和类别。
这是我得到的最接近的,但它不起作用,因为我将孩子分配给一级父母。
function page_walk($array, $parent_id = FALSE) { $organized_pages = array(); $children = array(); foreach($array as $index => $page) { if ( $page['parent_id'] == 0) // No, just spit it out and you're done { $organized_pages[$index] = $page; } else // If it does, { $organized_pages[$parent_id]['children'][$page['id']] = $this->page_walk($page, $parent_id); } } return $organized_pages; } function page_list($array) { $fakepages = array(); $fakepages[0] = array('id' => 1, 'parent_id' => 0, 'title' => 'Parent Page'); $fakepages[1] = array('id' => 2, 'parent_id' => 1, 'title' => 'Sub Page'); $fakepages[2] = array('id' => 3, 'parent_id' => 2, 'title' => 'Sub Sub Page'); $fakepages[3] = array('id' => 4, 'parent_id' => 3, 'title' => 'Another Parent Page'); $pages = $this->page_walk($fakepages, 0); print_r($pages); }
一些非常简单的通用树型build筑:
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; } } return $branch; } $tree = buildTree($rows);
该algorithm非常简单:
- 取所有元素的数组和当前父代的id(最初是
0
/ nothing /null
/ whatever)。 - 遍历所有元素。
- 如果元素的
parent_id
与您在1中获得的当前父级id相匹配,则该元素是父级的子级。 把它放在当前孩子的列表中(这里是$branch
)。 - 用3中刚刚标识的元素的idrecursion地调用该函数,即查找该元素的所有子元素,并将它们添加为
children
元素。 - 返回find的孩子的名单。
换句话说,该函数的一个执行返回给定父id的子元素的列表。 使用buildTree($myArray, 1)
调用它,它将返回一个包含父id为1的元素列表。起初这个函数被调用,父id为0,所以返回没有父id的元素,它们是根节点。 该函数recursion地调用自己来查找子对象的子对象。
我知道这个问题很老,但是我面临着一个非常类似的问题 – 除了数据量非常大。 经过一番努力之后,我设法在结果集的一遍中构build树 – 使用引用。 这段代码不是很漂亮,但是工作起来很快。 它是非recursion的 – 也就是说,结果集只有一遍,最后是一个array_filter
:
$dbh = new PDO(CONNECT_STRING, USERNAME, PASSWORD); $dbs = $dbh->query("SELECT n_id, n_parent_id from test_table order by n_parent_id, n_id"); $elems = array(); while(($row = $dbs->fetch(PDO::FETCH_ASSOC)) !== FALSE) { $row['children'] = array(); $vn = "row" . $row['n_id']; ${$vn} = $row; if(!is_null($row['n_parent_id'])) { $vp = "parent" . $row['n_parent_id']; if(isset($data[$row['n_parent_id']])) { ${$vp} = $data[$row['n_parent_id']]; } else { ${$vp} = array('n_id' => $row['n_parent_id'], 'n_parent_id' => null, 'children' => array()); $data[$row['n_parent_id']] = &${$vp}; } ${$vp}['children'][] = &${$vn}; $data[$row['n_parent_id']] = ${$vp}; } $data[$row['n_id']] = &${$vn}; } $dbs->closeCursor(); $result = array_filter($data, function($elem) { return is_null($elem['n_parent_id']); }); print_r($result);
当在这个数据上执行时:
mysql> select * from test_table; +------+-------------+ | n_id | n_parent_id | +------+-------------+ | 1 | NULL | | 2 | NULL | | 3 | 1 | | 4 | 1 | | 5 | 2 | | 6 | 2 | | 7 | 5 | | 8 | 5 | +------+-------------+
最后一个print_r
产生这个输出:
Array ( [1] => Array ( [n_id] => 1 [n_parent_id] => [children] => Array ( [3] => Array ( [n_id] => 3 [n_parent_id] => 1 [children] => Array ( ) ) [4] => Array ( [n_id] => 4 [n_parent_id] => 1 [children] => Array ( ) ) ) ) [2] => Array ( [n_id] => 2 [n_parent_id] => [children] => Array ( [5] => Array ( [n_id] => 5 [n_parent_id] => 2 [children] => Array ( [7] => Array ( [n_id] => 7 [n_parent_id] => 5 [children] => Array ( ) ) [8] => Array ( [n_id] => 8 [n_parent_id] => 5 [children] => Array ( ) ) ) ) [6] => Array ( [n_id] => 6 [n_parent_id] => 2 [children] => Array ( ) ) ) ) )
这正是我正在寻找的。
有可能使用PHP来获得MySQL的结果到数组,然后使用它。
$categoryArr = Array(); while($categoryRow = mysql_fetch_array($category_query_result)){ $categoryArr[] = array('parentid'=>$categoryRow['parent_id'], 'id'=>$categoryRow['id']); }
所有信用的接受的答案我想增加一些优化技巧大数组
function buildTree(&$elements, $parentId = 0) { //pass main array by reference $branch = array(); foreach ($elements as $key => $element) { if ($element['parent_id'] == $parentId) { unset($elements[$key]); $element['children'] = buildTree($elements, $element['id']); $branch[] = $element; } } return $branch; } $tree = buildTree($rows);
如果你传递数组作为引用,那么你可以取消那些在数组中find他们的位置的元素,所以父数组在每一个回合后都会变小
- 通过引用传递数组,所以未设置将影响所有的函数调用
即buildTree(&$elements, $parentId = 0)
- 在recursion调用
unset($elements[$key]);
之前find它的父项的未设置项目unset($elements[$key]);