Преобразование массива с использованием декларативного подхода

Учитывая следующий код:

$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]);
}

И его результат ($ иерархический):

 array (
10 =>
array (
'id' => '10',
'microtags' =>
array (
0 => 'hoho',
1 => 'ahha',
),
),
13 =>
array (
'id' => '13',
'microtags' =>
array (
),
),
)

Можно ли реорганизовать его в разумно эффективный декларативный / функциональный подход? Как использовать функции преобразования массива (map, Reduce, Filter и т. Д.)? Также без изменения ссылок или изменения одной и той же переменной. Если так, то как?

0

Решение

Создание и обход деревьев различной формы лучше всего достигается с помощью функций. Ниже мы создаем функции 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 ]));
}

Теперь мы можем увидеть гораздо более декларативное сокращение

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 может упростить нашу операцию сокращения. Однако такой подход может привести к множеству небольших отдельных функций. Другой подход заключается в изобретении вашего собственного типа данных, который удовлетворяет вашим потребностям. Ниже мы отбрасываем разделенные функции, которые мы создали выше, и обмениваем их на класс, 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 ();
}
2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]