创建和遍历不同形状的树最好使用函数来完成。下面,我们创建函数 node_create 和 node_add_child 来编码我们的意图。最后,我们使用array_reduce完成转换。 $flat 保持不变;我们的归约操作仅读取输入数据。
function node_create ($id, $children = []) {
return [ "id" => $id, "children" => $children ];
}
function node_add_child ($node, $child) {
return node_create ($node['id'], array_merge ($node['children'], [ $child ]));
}
$flat =
[ [ '10', 'hoho' ]
, [ '10', null ]
, [ '13', null ]
, [ '10', 'ahha' ]
];
$result =
array_reduce ($flat, function ($acc, $item) {
list ($id, $value) = $item;
if (! array_key_exists ($id, $acc))
$acc [$id] = node_create ($id);
if (! is_null ($value))
$acc [$id] = node_add_child ($acc [$id], $value);
return $acc;
}, []);
结果
print_r ($result);
// Array
// (
// [10] => Array
// (
// [id] => 10
// [children] => Array
// (
// [0] => hoho
// [1] => ahha
// )
// )
// [13] => Array
// (
// [id] => 13
// [children] => Array
// (
// )
// )
// )
上面,我们为$acc 使用了关联数组,这意味着我们必须使用PHP 的内置函数来与关联数组进行交互。我们可以抽象出 PHP 丑陋的、非功能性的接口以获得更有利的接口。
function has ($map, $key) {
return array_key_exists ($key, $map);
}
function get ($map, $key) {
return $map [$key];
}
function set ($map, $key, $value = null) {
$map [$key] = $value;
return $map;
}
我们将添加null 子级的逻辑移至node_add_child
function node_create ($id, $children = []) {
return [ "id" => $id, "children" => $children ];
}
function node_add_child ($node, $child = null) {
if (is_null ($child))
return $node;
else
return node_create ($node['id'], array_merge ($node['children'], [ $child ]));
}
现在我们可以看到一个更具声明性的 reduce
function make_tree ($flat = []) {
return
array_reduce ($flat, function ($acc, $item) {
list ($id, $value) = $item;
return
set ( $acc
, $id
, has ($acc, $id)
? node_add_child (get ($acc, $id), $value)
: node_add_child (node_create ($id), $value)
);
}, []);
}
print_r (make_tree ($flat));
// same output as above
在上面,我们看到has、get 和set 如何简化我们的reduce 操作。但是,这种方法可能会导致许多小的、分离的功能。另一种方法涉及发明您自己的满足您需求的数据类型。下面,我们废弃上面创建的分离函数,并将它们换成一个类,MutableMap
class MutableMap {
public function __construct ($data = []) {
$this->data = $data;
}
public function has ($key) {
return array_key_exists ($key, $this->data);
}
public function get ($key) {
return $this->has ($key)
? $this->data [$key]
: null
;
}
public function set ($key, $value = null) {
$this->data [$key] = $value;
return $this;
}
public function to_assoc () {
return $this->data;
}
}
现在我们不必将$acc 传递给每个函数,而是将其替换为$map,这是我们新类型的一个实例
function make_tree ($flat = []) {
return
array_reduce ($flat, function ($map, $item) {
list ($id, $value) = $item;
return
$map -> set ( $id
, $map -> has ($id)
? node_add_child ($map -> get ($id), $value)
: node_add_child (node_create ($id), $value)
);
}, new MutableMap ())
-> to_assoc ();
}
当然,您可以将node_create 和node_add_child 换成基于类的实现class Node { ... }。这个练习留给读者。
function make_tree ($flat = []) {
return
array_reduce ($flat, function ($map, $item) {
list ($id, $value) = $item;
return
$map -> set ( $id
, $map -> has ($id)
? $map -> get ($id) -> add_child ($value)
: (new Node ($id)) -> add_child ($value)
);
}, new MutableMap ())
-> to_assoc ();
}