使用声明性方法转换数组
Transform array using a declarative approach
给定以下代码:
$flat = [
[ '10', 'hoho'],
[ '10', null],
[ '13', null],
[ '10', 'ahha']
];
//imperative, procedural approach
$hierarchical = [];
foreach ($flat as $entry) {
$id = $entry[0];
$hierarchical[$id]['id'] = $id;
$hierarchical[$id]['microtags'] = $hierarchical[$id]['microtags'] ?? [];
if ($entry[1] != null)
array_push($hierarchical[$id]['microtags'], $entry[1]);
}
及其结果($hierarchical):
array (
10 =>
array (
'id' => '10',
'microtags' =>
array (
0 => 'hoho',
1 => 'ahha',
),
),
13 =>
array (
'id' => '13',
'microtags' =>
array (
),
),
)
是否可以将其重构为合理有效的 declarative/functional 方法?喜欢使用数组转换函数(map、reduce、filter 等)?也无需更改引用或更改相同的变量。如果可以,怎么做?
创建和遍历不同形状的树最好使用函数来完成。下面,我们创建函数 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的built-in函数来与关联数组交互。我们可以抽象掉 PHP 的丑陋接口,non-functional 接口以获得更有利的接口。
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
children 的逻辑移动到 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 操作。然而,这种方法会导致许多小的、分离的功能。另一种方法涉及发明满足您需要的您自己的数据类型。下面,我们废弃上面创建的分离函数并将它们换成 class、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-based 实现,class Node { ... }
。此练习留给 reader.
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 ();
}
给定以下代码:
$flat = [
[ '10', 'hoho'],
[ '10', null],
[ '13', null],
[ '10', 'ahha']
];
//imperative, procedural approach
$hierarchical = [];
foreach ($flat as $entry) {
$id = $entry[0];
$hierarchical[$id]['id'] = $id;
$hierarchical[$id]['microtags'] = $hierarchical[$id]['microtags'] ?? [];
if ($entry[1] != null)
array_push($hierarchical[$id]['microtags'], $entry[1]);
}
及其结果($hierarchical):
array (
10 =>
array (
'id' => '10',
'microtags' =>
array (
0 => 'hoho',
1 => 'ahha',
),
),
13 =>
array (
'id' => '13',
'microtags' =>
array (
),
),
)
是否可以将其重构为合理有效的 declarative/functional 方法?喜欢使用数组转换函数(map、reduce、filter 等)?也无需更改引用或更改相同的变量。如果可以,怎么做?
创建和遍历不同形状的树最好使用函数来完成。下面,我们创建函数 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的built-in函数来与关联数组交互。我们可以抽象掉 PHP 的丑陋接口,non-functional 接口以获得更有利的接口。
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
children 的逻辑移动到 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 操作。然而,这种方法会导致许多小的、分离的功能。另一种方法涉及发明满足您需要的您自己的数据类型。下面,我们废弃上面创建的分离函数并将它们换成 class、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-based 实现,class Node { ... }
。此练习留给 reader.
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 ();
}