如何解决PHP 平面对象到分层对象
关于该主题已经有一些问题,但它们通常与将分层对象变成平面对象有关,这对我来说不是问题。但是,我目前完全无法从平面对象创建分层对象。
假设有以下简化的数据库表
id | parent
1 | 0
2 | 1
3 | 1
4 | 3
5 | 4
[{id:1,parent:0},{id:2,parent:1},{id:3,{id:4,parent:3},{id:5,parent:4}]
[{
id:1,children:[
{id:2},children:[
{id:4,children:[
{id:5}
]
}
]
}
]
}]
我有一些开始,但我的大脑跟不上... 有没有人对返回所需对象的递归函数有任何建议? 下面是我目前拥有的脏代码(想想涂鸦)。它正常运行,直到递归调用该函数并开始复制条目。
function CreateObject($object,$new = array()){
for($i = 0; $i < count($object); $i++){
$found = false;
foreach($object[$i] as $key => $value){
if ($key == 'parent'){
if ($value == 0){
array_push($new,$object[$i]);
} else {
//push into parent
for($j = 0; $j < count($new); $j++){
foreach($new[$j] as $k => $v){
if ($k == 'id' && $v == $value){
$found = true;
if (property_exists($new[$j],'children')){
array_push($new[$j]->children,$object[$i]);
} else {
$new[$j]->children = array();
array_push($new[$j]->children,$object[$i]);
}
}
}
if (!$found){
foreach($new[$j] as $k => $v){
if ($k == 'children'){
CreateObject($object,$v);
}
}
break;
}
}
}
}
}
}
return $new;
}
$hierarchalObject = CreateObject($flatObject);
$flatObject = array(
array('id' => 1,'parent' => 0),array('id' => 2,'parent' => 1),array('id' => 5,'parent' => 2),array('id' => 6,);
$flatObject = json_decode(json_encode($flatObject));
返回:
[
{
"id": 1,"parent": 0,"children": [
{
"id": 2,"parent": 1,"children": [
{
"id": 5,"parent": 2
},{
"id": 6,{ //DUPLICATES HERE
"id": 5,"parent": 2
}
]
}
]
}
]
我尽量做到完整。对于我疼痛的大脑,任何帮助将不胜感激。
更新
感谢 C3iG3 的回答,以下功能非常适合我的情况。这是 C3iG3 答案的一个非常轻微的修改版本,包括所有节点的属性,如果为空,则不包括 children 属性。
<?PHP
function unflatten ( $data )
{
/* Create new node objects */
function create_new_node ( $o ) {
return new class ( $o ) {
public $id;
public function __construct ( $o ) {
//get all properties and assign to node
foreach($o as $key => $value){
if ($key != 'parent'){
$this->$key = $value;
}
}
}
};
}
$roots = array(); // store root nodes
$nodes = array(); // store all nodes
foreach ( $data as $o )
{
$node = isset( $nodes[$o->id] ) ? $nodes[$o->id] : null; // check if node was already created
if ( !$node ) {
$node = create_new_node($o);
}
$nodes[$o->id] = $node; // keep track of node
if ( $o->parent ) // node has a parent
{
if ( !isset( $nodes[$o->parent] ) ) { // if parent node was not already created
$nodes[$o->parent] = create_new_node( $o->parent );
}
$nodes[$o->parent]->children[] = $node; // add node to parent
} else {
$roots[] = $node;
}
}
return $roots;
}
?>
解决方法
这个问题不太适合递归解决。这样做的原因主要是因为您的数据是如何构建的。您的每个对象都跟踪其父对象,而不是其子对象。
递归函数没有明显的起点。如果从父对象开始,您不知道将哪些子对象绑定到父对象,因此无法划分问题(无法向下递归层次结构)。您可以向上递归层次结构(通过跟随父级),但是由于这不会以任何方式划分问题,因此递归编写它不会受益。您当然可以编写一个递归解决方案来创建节点、遍历现有层次结构并插入它们,但这对于大型层次结构会变得效率低下。
相反,这个问题更适合迭代解决方案。
<?php
function unflatten ( $data )
{
/* Create new node objects */
function create_new_node ( $id ) {
return new class ( $id ) {
public $id;
public $children = array();
public function __construct ( $id ) { $this->id = $id; }
};
}
$roots = array(); // store root nodes
$nodes = array(); // store all nodes
foreach ( $data as $o )
{
$node = isset( $nodes[$o->id] ) ? $nodes[$o->id] : null; // check if node was already created
if ( !$node ) {
$node = create_new_node( $o->id );
}
$nodes[$o->id] = $node; // keep track of node
if ( $o->parent ) // node has a parent
{
if ( !isset( $nodes[$o->parent] ) ) { // if parent node was not already created
$nodes[$o->parent] = create_new_node( $o->parent );
}
$nodes[$o->parent]->children[] = $node; // add node to parent
} else {
$roots[] = $node;
}
}
return $roots;
}
$queryResult = json_decode('[{"id":1,"parent":0},{"id":2,"parent":1},{"id":3,{"id":4,"parent":3},{"id":5,"parent":4}]' );
echo json_encode( unflatten( $queryResult ) );
这将生成以下结构:
[{
"id": 1,"children": [{
"id": 2,"children": []
},{
"id": 3,"children": [{
"id": 4,"children": [{
"id": 5,"children": []
}]
}]
}]
}]
此解决方案需要对原始对象集进行一次传递,并在恒定时间内将它们附加到其父对象。必须跟踪 nodes
数组中的所有现有节点会产生一些轻微的开销。
这个解决方案可以重写为递归,但最终会建模一个循环结构(这是一个很好的指标,表明问题更适合迭代编写)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。