web-dev-qa-db-ja.com

PHPのフラット配列からツリーを構築する

私はインターネットを見回しましたが、探しているものがまだ見つかりませんでした。各要素に「id」と「parent_id」を含むフラット配列があります。各要素には1つの親しかありませんが、複数の子を持つことができます。 parent_id = 0の場合、ルートレベルのアイテムと見なされます。フラット配列をツリーに入れようとしています。私が見つけた他のサンプルは、要素を親にコピーするだけですが、元のものはまだ存在します。

[〜#〜] edit [〜#〜]

開始配列の各要素は、個別のXMLファイルから読み取られます。ファイルに親がない場合、ファイル自体はparent_idの値として「0」になります。キーは実際には文字列です。

先ほどの混乱でごめんなさい。うまくいけば、これはより明確です:

/ EDIT

私の開始配列:

 Array 
(
 [_319_] => Array 
(
 [id] => 0 
 [parent_id] => 0 
)
 
 [_320_] =>配列
(
 [id] => _320 _ 
 [parent_id] => 0 
)
 
 [_321_] =>配列
(
 [id] => _321 _ 
 [parent_id] => _320 _ 
)
 
 [_322_] =>配列
(
 [id] => _322 _ 
 [parent_id] => _321 _ 
)
 
 [_323_] =>配列
(
 [id] => _323 _ 
 [parent_id] => 0 
)
 
 [_324_] =>配列
(
 [id] => _324 _ 
 [parent_id] => _323 _ 
)
 
 [_325_] =>配列
(
 [id] => _325 _ 
 [parent_id] => _320 _ 
)
)

ツリーが作成された後の結果の配列:

 Array 
(
 [_319_] => Array 
(
 [id] => _319 _ 
 [parent_id] => 0 
)
 
 [_320_] =>配列
(
 [id] => _320 _ 
 [parent_id] => 0 
 [children] => Array 
(
 [_321_] => Array 
(
 [id] => _321 _ 
 [parent_id] => _320 _ 
 [children] => Array 
(
 [_322_] => Array 
(
 [id] = > _322 _ 
 [parent_id] => _321 _ 
)
)
)
 [_325_] =>配列
(
 [id] => _325 _ 
 [parent_id] => _320 _ 
)
)
 [_323_] =>配列
(
 [id] => _323 _ 
 [parent_id] => 0 
 [子供] =>配列
(
 [_324_] =>配列
(
 [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; 
} 
 
34
DSkinner

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;
}
51
n0nag0n

ImmortalFireflyによる解決策は機能していますが、mrddedが指摘するように、子供のいない最初の親を救うことはできません。この問題を修正するために関数を編集しました:

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;
}
27
SteveEdson

これは私のために働く:

$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);
6
Eugen Rieck

私はロジックを見ることができ、結果でこれを保存します:

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

ルートとしての「マジック」番号と既存のIDの両方として0を悪用したため、id = 0ブランチで再帰が行われるようになりました。 _$tree[$item['parent_id']]['children'][] = &$tree[$item['id']];_の前にif($item['parent_id']!=$item['id'])を追加すると、それを防ぐことができますが、きれいではありません。

3
Wrikken

この関数(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>';
  1. より効率的な階層システム
2
Gigamegs

これは古い質問ですが、答えをここに投稿します。

/* 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ですが、簡単に作成できます。

2
Inglis Baderson

元のデータ構造に要素の親が存在しない場合を除き、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;
}
1
Arne M

いくつかの問題を解決するはずなので、MySQLで階層データを保存およびロードすることを検討します。最初の配列はデータベースから直接取得したデータを表すと仮定していますか?

隣接モデルを使用してデータを階層構造に整理しようとしているようです。ネスティングを使用してこれを達成する他の方法もあります。このデータをデータベースから取得していない場合、これはそれほど有用ではない可能性があります。

このリンクはあなたを助けるはずです: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

1
Daniel West

最上位の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);
        }
    }
}
0
user628176

きれいで、短く、バラストがありません。ツリーへの配列の配列:

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

これが私のソリューションです。他のソリューションをコピーして最適化します。

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;
}
0
touzas

@ eugen-rieckと同様のソリューションを思いつき、それを共有したいと考えました。私は$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];
    }
}
0
hoorider