PHP 通过父 ID 将数组转换为多维数组


php convert array to multidimensional array by parent id

我有一个包含 stdclasses 的一维数组,这些对象有一个名称和一个 "abh" 属性。现在我想得到一个像多维数组这样的树视图。假设我的数组如下所示:

$ar = array(
    (object)array("name" => "Test1","otherstuff"=>array(),"abh"=>""),
    (object)array("name" => "Test2","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test3","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test4","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test5","otherstuff"=>array(),"abh"=>"Test1"),
    (object)array("name" => "Test6","otherstuff"=>array(),"abh"=>"Test5"),
    (object)array("name" => "Test7","otherstuff"=>array(),"abh"=>"Test5"),
    (object)array("name" => "Test8","otherstuff"=>array(),"abh"=>array("Test5","Test7")),
    (object)array("name" => "Test9","otherstuff"=>array(),"abh"=>"Test8"),
    (object)array("name" => "Test10","otherstuff"=>array(),"abh"=>"Test6"),
    (object)array("name" => "Test11","otherstuff"=>array(),"abh"=>"Test9"),
);

数据来自之前调用的函数,而不是来自数据库。结果是这样的:

Array
(
[0] => stdClass Object
    (
        [name] => Test1
        [otherstuff] => Array
            (
            )
        [abh] => 
    )
[1] => stdClass Object
    (
        [name] => Test2
        [otherstuff] => Array
            (
            )
        [abh] => Test1
    )
[...]
)

所以现在,我希望这个数组将按"ABH"属性排序。 第一个"ABH"为空的地方,它是根,所有其他的都将成为它的子级。 因此,该函数必须将"childs"属性添加到主IF中,它具有子级。但是现在这个是特殊的类型,因为"abh"可以是一个数组,并且可以重新分配给n-parent。在我上面的例子中,Test8应该是Test5和Test7(克隆(的子级。

我试图用递归函数和 foreach 中的循环来处理这个问题。在那里,它检查元素的 abh 与存在的父元素相对应,但不起作用。array_filter(( 不会像我想要的那样做。

所以,我想要这个结果:

Array
(
    [Test1] => stdClass Object
        (
            [name] => Test1
            [otherstuff] => Array()
            [abh] => 
            [childs] => Array
                (
                    [Test2] => stdClass Object
                        [...] and so on
                )
        )
)

对此有什么想法吗?我也不知道返回的数组将有多少"级别",即存在多少输入对象和"abh"分配。秒是"abh"数组最多可以有 10 个"父项"。

感谢您的任何帮助!


作为请求我为该"分拣机"的非工作原型

function checkABH(&$return,&$ar,$deph) {
    // clone all with array-abh
    $count = count($ar);
    for($x=0;$x<$count;$x++) {#$ar as$key=>$val) {
        if(is_array($ar[$x]->abh)) {
            $clone = $ar[$x];
            foreach($ar[$x]->abh as$abh) {
                #echo "<br>$x @@ $abh";
                $clone->abh = $abh;
                $ar[]       = $clone;
                #echo '<pre>'.print_r($clone,true).'</pre>';
            }
            // delete array-abh-element
            unset($ar[$x]);
        }
    }
    echo '<pre>'.print_r($ar,true).'</pre>';
    echo '</div>';
    echo '<div style="float:left;width:auto;margin:0px 10px;"><h3>Result:</h3>';
    // pass to sorter
    checkABH_a($return,$ar,$deph);
}
function checkABH_a(&$return,&$ar,$deph) {
    $test_abhs = array();
    foreach($ar as$key=>$val) {
        $val->childs = array();
        if(isset($return[$deph])&&isset($return[$deph]->name)&&$val->abh==$return[$deph]->name) {
            $return[$deph]->childs[] = $val;
            unset($ar[$key]);
        } elseif($val->abh==$deph) {
            $return[$val->abh] = $val;
            unset($ar[$key]);
        } else {
            $test_abhs[] = $val->abh;
        }
    }
    if(count($test_abhs)>0) {
        $test_abhs = array_unique($test_abhs);
        #echo '<pre>'.print_r($test_abhs,true).'</pre>';
        foreach($test_abhs as$abh) {
            checkABH_a($return,$ar,$abh);
        }
    }
}
echo '<div style="float:left;width:260px;border-right:1px solid #cccccc;margin:0px 10px;"><h3>Input</h3>';
echo '<pre>'.print_r($ar,true).'</pre>';
echo '</div>';
echo '<div style="float:left;width:260px;border-right:1px solid #cccccc;margin:0px 10px;"><h3>Cloned:</h3>';
$return = array();
checkABH($return,$ar,"");
echo'<pre>'.print_r($return,true).'</pre>';
echo '</div>';

这些递归树并没有变得更容易,随着"多父"要求的添加;-/无论如何,它看起来仍然很有趣...

我不打算在这里记录所有代码,因为我认为我已经足够注释了。

笔记:

  • 将"多个父项"转换(复制(为单个条目,因此"insertNode"函数更容易理解。
  • 我没有使用"stdClass",而是创建了一个名为"TreeNode"的类,因为我可能想稍后添加方法。

使用 PHP 5.3.18 Viper-7.com 代码的工作版本

任何关于代码的查询都会发表评论,我会尝试回答他们。

<?php // https://stackoverflow.com/questions/27360813/php-convert-array-to-multidimensional-array-by-parent-id
$ar = array(
    (object) array("name" => "Test1", "otherstuff"=>array('parent is Test1'), "abh"=>""),
    (object) array("name" => "Test2", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test3", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test4", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test5", "otherstuff"=>array('parent is Test1'), "abh"=>"Test1"),
    (object) array("name" => "Test6", "otherstuff"=>array('parent is Test5'), "abh"=>"Test5"),
    (object) array("name" => "Test7", "otherstuff"=>array('parent is Test5'), "abh"=>"Test5"),
    (object) array("name" => "Test8", "otherstuff"=>array('parent is Test5 AND Test7'), "abh"=>array("Test5", "Test7")),
    (object) array("name" => "Test9", "otherstuff"=>array('parent is Test8'), "abh"=>"Test8"),
    (object) array("name" => "Test10", "otherstuff"=>array('parent is Test6'), "abh"=>"Test6"),
    (object) array("name" => "Test11", "otherstuff"=>array('parent is Test9'), "abh"=>"Test9"),
);

/*
 * The requirement is that for any child then the parent must be in the tree already.
 */
// Convert those parent with 'arrays of parents' into individual entries as it will make the insert
// easier to understand.
// i will also convert all the input into a 'TreeNode' class while i am doing this pass down the input. 
// i can add some methods later if i wish.
// i will also get the root of the tree while i am doing this...
$rootNode = new TreeNode(array_shift($ar));
$treeNodeList = array();
foreach($ar as $node) {
    if (is_array($node->abh)) { // generate duplicate nodes
        foreach($node->abh as $parentName) {
            $node->abh = $parentName; // make it an ordinary node
            $treeNodeList[] = new TreeNode($node);
        }
        continue;
    }
    $treeNodeList[] = new TreeNode($node);
}
// var_dump($ar, $rootNode, $treeNodeList);
// Ok, we now have a node list in the appropriate order - let us build the tree
/**
 * This is a 'multiway' tree .i.e. there can be any number of 'child' nodes
 *                                 for any parent
 * Usual rules:
 *   o The parent MUST be in the tree already.
 *   o Children are added, by searching from the root of the tree, in this version
 *
 */
// the output will be here
$theTree = array($rootNode->name => $rootNode);
foreach ($treeNodeList as $childNode) { // add it
    $inserted = insertNode($rootNode, $childNode);
    if (!$inserted) {
      var_dump('Unable to insert:', $childNode, ' in tree:', $rootNode);
      die('i am here: '. __FILE__.__LINE__);
    }
}
// show the tree
echo '<pre>';
   print_r($theTree);
echo '</pre>';
exit;
// --------- end of processing -------------
/**
 * Insert the node in the tree
 *
 * @param TreeNode $parent
 * @param TreeNode $newChild
 */
function insertNode(TreeNode &$parent, TreeNode $newChild) {
    if ($newChild->abh === $parent->name) { // add it to the current parent
        $parent->children[$newChild->name] = $newChild; // add child
        return true;
    }
    // check the children
    foreach($parent->children as $newParent) {
       if (insertNode($newParent, $newChild)) {
          return true;
       }
    }
    return false; // unable to insert child in the tree
}
// -----------------------------------------------------------------------
/**
 * treeNode: (Data and Relationships)
 *   nodeId:            'name'
 *   nodeData:          'otherstuff'
 *   nodeParentList:    'abh' - may be a list of parents.
 *                          Note: In this version of the code: All the parents
 *                                MUST be in the tree already.
 *   children:          array of treeNode - recursive structure
 */
// i will make the properties all public to make it easier to debug.
class TreeNode  {
    public $name = '';
    public $otherstuff = array();
    public $abh = '';
    public $children = array(); // list of child nodes
    public function __construct(stdClass $node)
    {
        $this->name = $node->name;
        $this->otherstuff = $node->otherstuff;
        $this->abh = $node->abh;
    }
} // class end --------------------------