<?php
namespace Topxia\Common;
/**
* Class Tree 多叉树 数据结构类
* 对树的操作是递归的,如有特殊需求导致递归会爆栈 可以改用stack来实现递归的效果
* @package Topxia\Common
*/
class Tree
{
/**
* @var array<Tree>
*/
private $children = array();
/**
* @var Tree
*/
private $parent;
/**
* @var mixed
*/
public $data;
public function __construct($data = array(), Tree $parent = null)
{
$this->data = $data;
$this->parent = $parent;
}
/**
* Like ArrayToolkit::column
* @param $key
* @return mixed
*/
public function column($key)
{
return $this->reduce(function ($ret, $tree) use ($key){
if(!empty($tree->data[$key])){
array_push($ret, $tree->data[$key]);
}
return $ret;
}, array());
}
/**
* each Tree Node call closure
* @param \Closure $closure
* @return $this
*/
public function each(\Closure $closure)
{
$closure($this);
foreach ($this->getChildren() as $child) {
$child->each($closure);
}
return $this;
}
/**
* Like array_reduce
* @see http://php.net/manual/zh/function.array-reduce.php
* @param \Closure $closure
* @param null $initial
* @return mixed
*/
public function reduce(\Closure $closure, $initial = null)
{
is_null($initial) ? $ret = $this : $ret = $initial;
$ret = $closure($ret ,$this);
foreach ($this->children as $child){
$ret = $child->reduce($closure, $ret);
}
return $ret;
}
/**
* @return array<type($this->data)>
*/
public function toArray()
{
$ret = $this->data;
$ret['children'] = array();
foreach ($this->getChildren() as $child) {
array_push($ret['children'], $child->toArray());
}
return $ret;
}
/**
* @param \Closure $closure
* @return Tree
*/
public function find(\Closure $closure)
{
if($closure($this)){
return $this;
}
$ret = null;
foreach ($this->children as $child){
$ret = $child->find($closure);
if(!is_null($ret)){
break;
}
}
return $ret;
}
public function findToParent(\Closure $closure)
{
$parent = $this;
$ret = array();
while (!is_null($parent)){
if($closure($parent)){
$ret[] = $parent;
}
$parent = $parent->getParent();
}
return array_pop($ret);
}
public static function buildWithArray(array $input, $rootId = 0, $key = 'id', $parentKey = 'parentId')
{
$root = new self(array(
$key => $rootId
));
// 方便找到父节点
$map = array(
$rootId => $root
);
$buildingArray = $input;
while (!empty($buildingArray)) {
$buildingCount = count($buildingArray);
foreach ($buildingArray as $index => $value) {
if (isset($map[$value[$parentKey]])) {
$parent = $map[$value[$parentKey]];
$tree = new Tree($value, $parent);
$parent->addChild($tree);
$map[$value[$key]] = $tree;
unset($buildingArray[$index]);
}
}
//一次构建树后剩下元素不变。 说明这些元素的父节点不存在树的节点里,是构建不出的树的
if($buildingCount === count($buildingArray)){
break;
}
}
return $root;
}
/**
* @param Tree $child
* @return $this
*/
public function addChild(Tree $child)
{
array_push($this->children, $child);
return $this;
}
/**
* @return array<Tree>
*/
public function getChildren()
{
return $this->children;
}
/**
* @return Tree
*/
public function getParent()
{
return $this->parent;
}
/**
* @param Tree $parent
* @return $this
*/
public function setParent(Tree $parent)
{
$this->parent = $parent;
return $this;
}
public function hasChildren()
{
return !empty($this->children);
}
}