[英]Build a tree from a flat array in PHP
我環顧了互聯網,並沒有完全找到我要找的東西。 我有一個平面數組,每個元素都包含一個“id”和一個“parent_id”。 每個元素只有一個父元素,但可能有多個子元素。 如果 parent_id = 0,則將其視為根級別項目。 我正在嘗試將我的平面陣列放入一棵樹中。 我發現的其他示例僅將元素復制到父元素,但原始元素仍然存在。
編輯
起始數組的每個元素都是從一個單獨的 XML 文件中讀取的。 如果文件沒有父級,則文件本身的 parent_id 值將為“0”。 鍵實際上是字符串。
我很抱歉之前的混亂。 希望這更清楚:
/編輯
我的起始數組:
Array ( [_319_] => Array ( [id] => 0 [parent_id] => 0 ) [_320_] => Array ( [id] => _320_ [parent_id] => 0 ) [_321_] => Array ( [id] => _321_ [parent_id] => _320_ ) [_322_] => Array ( [id] => _322_ [parent_id] => _321_ ) [_323_] => Array ( [id] => _323_ [parent_id] => 0 ) [_324_] => Array ( [id] => _324_ [parent_id] => _323_ ) [_325_] => Array ( [id] => _325_ [parent_id] => _320_ ) )
生成樹后的結果數組:
Array ( [_319_] => Array ( [id] => _319_ [parent_id] => 0 ) [_320_] => Array ( [id] => _320_ [parent_id] => 0 [children] => Array ( [_321_] => Array ( [id] => _321_ [parent_id] => _320_ [children] => Array ( [_322_] => Array ( [id] => _322_ [parent_id] => _321_ ) ) ) [_325_] => Array ( [id] => _325_ [parent_id] => _320_ ) ) [_323_] => Array ( [id] => _323_ [parent_id] => 0 [children] => Array ( [_324_] => Array ( [id] => _324_ [parent_id] => _323_ ) ) )
非常感謝任何幫助/指導!
到目前為止我擁有的一些代碼:
function buildTree(array &$elements, $parentId = 0) { $branch = array(); foreach ($elements as $element) { if ($element['parent_id'] == $parentId) { $children = $this->buildTree($elements, $element['id']); if ($children) { $element['children'] = $children; } $branch[] = $element; } } return $branch; }
你忘記了那里的unset()
兄弟。
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;
}
ImmortalFirefly 的解決方案正在奏效,然而,正如mrded 指出的那樣,它並不能拯救沒有孩子的第一個父母。 我編輯了函數來解決這個問題:
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($element);
}
}
return $branch;
}
這對我有用:
$index=array();
$tree=array();
foreach ($ori as $key=>$var) {
$var=array_shift($ori);
if ($var['id']==0) $var['id']=$key;
if ((string)$var['parent_id']==='0') {
$tree[$key]=$var;
$index[$key]=&$tree[$key];
} else if (isset($index[$var['parent_id']])) {
if (!isset($index[$var['parent_id']]['children'])) $index[$var['parent_id']]['children']=array();
$index[$var['parent_id']]['children'][$key]=$var;
$index[$key]=&$index[$var['parent_id']]['children'][$key];
} else {
array_push($ori,$var);
}
}
unset($index);
print_r($tree);
我可以看到邏輯,在結果中保存:
Array
(
[0] => Array
(
[id] => 0
[parent_id] => 0
)
[1] => Array
(
[id] => 1
[parent_id] => 0
)
恕我直言,parent_id = o,這里 [1] 不應該是 [0] 的孩子嗎?
無論如何,參考救援:
$tree = array();
foreach($inputarray as $item){
if(!isset($tree[$item['id']])) $tree[$item['id']] = array();
$tree[$item['id']] = array_merge($tree[$item['id']],$item);
if(!isset($tree[$item['parent_id']])) $tree[$item['parent_id']] = array();
if(!isset($tree[$item['parent_id']]['children'])) $tree[$item['parent_id']]['children'] = array();
$tree[$item['parent_id']]['children'][] = &$tree[$item['id']];
}
$result = $tree[0]['children'];
unset($tree);
print_r($result);
因為您已經濫用 0 作為根的“神奇”數字和現有 id,我們現在在 id=0 分支中有遞歸。 在$tree[$item['parent_id']]['children'][] = &$tree[$item['id']];
if($item['parent_id']!=$item['id'])
之前添加if($item['parent_id']!=$item['id'])
$tree[$item['parent_id']]['children'][] = &$tree[$item['id']];
可以防止這種情況,但它並不漂亮。
可以使用此函數(parent_id,id,title)構造稍微不同的源數組:
$q = mysql_query("SELECT id, parent_id, name FROM categories");
while ($r = mysql_fetch_row($q)) {
$names[$r[0]] = $r[2];
$children[$r[0]][] = $r[1];
}
function render_select($root=0, $level=-1) {
global $names, $children;
if ($root != 0)
echo '<option>' . strrep(' ', $level) . $names[$root] . '</option>';
foreach ($children[$root] as $child)
render_select($child, $level+1);
}
echo '<select>';
render_select();
echo '</select>';
雖然這是一個老問題,但我會在這里發布我的答案:
/* assuming top level pid = 0 */
$rows = array (
array ( 'id' => 1, 'pid' => 0 ),
/* ... */
);
/* make id become array key */
$rows = array_column ( $rows, null, 'id' );
foreach ( $rows as $key => $val ) {
if ( $val ['pid'] ) {
if ( isset ( $rows [$val ['pid']] )) {
$rows [$val ['pid']]['children'][] = &$rows [$key];
}
}
}
foreach ( $rows as $key => $val ) {
if ( $val ['pid'] ) unset ( $rows [$key] );
}
array_column
是 PHP 5.5,但您可以輕松制作自己的。
SteveEdson 的代碼工作正常,除非元素的父元素在原始數據結構中不存在。 這是我對此的修復(但是,它從元素中刪除了“parent_id”,這可能會也可能不可接受):
function buildTree(array &$elements, $parentId = 0)
{
$branch = array();
foreach ($elements as &$element) {
if ($element["parent_id"] != null && $elements[$element["parent_id"]] == null)
unset($element["parent_id"]);
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[$element['id']] = $element;
unset($element);
}
}
return $branch;
}
您想在 MySQL 中存儲和加載分層數據,因為這應該可以解決一些問題。 我假設第一個數組代表直接從數據庫中獲取的數據?
看起來您正在嘗試使用鄰接模型將數據組織到層次結構中。 還有其他方法可以使用嵌套來實現這一點。 如果您不是從數據庫中獲取這些數據,那么這可能沒有那么有用。
此鏈接應該可以幫助您: http : //mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
這是我的解決方案,理想情況下,如果我們假設頂級 parent_id=0:
function MakeTree($arr){
$parents_arr=array();
foreach ($arr as $key => $value) {
$parents_arr[$value['pid']][$value['id']]=$value;
}
$tree=$parents_arr['0'];
$this->createTree($tree, $parents_arr);
return $tree;
}
function createTree(&$tree, $parents_arr){
foreach ($tree as $key => $value) {
if(!isset($value['children'])) {
$tree[$key]['children']=array();
}
if(array_key_exists($key, $parents_arr)){
$tree[$key]['children']=$parents_arr[$key];
$this->createTree($tree[$key]['children'], $parents_arr);
}
}
}
這是我的解決方案,復制並優化其他解決方案。
function buildTree(array &$elements, $parentId = 0) {
$branch = array();
foreach ($elements as $key => $element) {
if ($element['parent_id'] == $parentId) {
$children = $this->buildTree($elements, $key);
if ($children) {
$element['children'] = $children;
}
$branch[$key] = $element;
unset($elements[$key]);
}
}
return $branch;
}
清潔、短小且無鎮流器。 要樹的數組數組:
class Mother {
private $root;
public function treeInit($array)
{
$this->root = new Child();
foreach($array as $value){
$this->root->treeClimb(array_reverse($value));
}
return $this->root;
}
}
class Child {
private $children = [];
public function treeClimb($arr)
{
if(count($arr) > 0) {
$childTmp = array_pop($arr);
if(!key_exists($childTmp,$this->children))
{
$this->children[$childTmp] = new Child();
}
$this->children[$childTmp]->treeClimb($arr);
}
}
}
$array = array(array('obst','banae','krumm','gelb'),
array('obst','beere','him'),
array('obst','beere','brom'),
array('obst','banae','gerade'),
array('veg','carot','gerade'));
$obj = new Mother();
var_dump($obj->treeInit($array));
我想出了一個與@eugen-rieck 類似的解決方案,並想分享它。 不過,我將$branches
命名$branches
我的索引數組。
$tree = [];
$branches = [];
while (!empty($input)) {
$beforeCount = count($input);
foreach ($input as $id => $item) {
$pid = $item['parent_id'];
if (isset($branches[$pid])) {
$branches[$pid]['children'][$id] = $item;
$branches[$id] = &$branches[$pid]['children'][$id];
unset($input[$id]);
}
}
if ($beforeCount === count($input)) {
$firstItem = array_shift($input);
$id = $firstItem['id'];
$tree[$id] = $firstItem;
$branches[$id] = &$tree[$id];
}
}
這是我的解決方案,它首先按parent_id
項目進行分組,然后從根遞歸填充所有子分支,使用分組列表進行查找。
public function get_nested_tree() {
$parent_node = null;
$nodes_by_parent = array();
if(is_null($flat_list) || count($flat_list) <= 0){
return null;
}
foreach ($flat_list as $node) {
if($node['parent_id'] != null){
$nodes_by_parent[$node['parent_id']][] = $node;
}
else{
// NB. In my implementation if multiple roots exist,
// I want to always return the first...
if(is_null($parent_node)){
$parent_node = $node;
}
}
}
return $this->populate_branch($parent_node, $nodes_by_parent);
}
public function populate_branch($node, $nodes_by_parent){
$children = $nodes_by_parent[$node['id']] ?? [];
foreach ($children as &$child){
$child = $this->populate_branch($child, $nodes_by_parent);
}
$node['children'] = $children;
return $node;
}
我相信時間復雜度是線性的( O(n)
)——假設 PHP 關聯數組等價於HashMap
或其他語言的Dictionary
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.