Teniendo esta matriz:
[ "id" => 5, "name" => "Item 5", "all_parents" => [ "id" => 4, "name" => "Item 4", "all_parents" => [ "id" => 3, "name" => "Item 3", "all_parents" => [ "id" => 2, "name" => "Item 2", "all_parents" => [ "id" => 1, "name" => "Item 1", "all_parents" => null ] ] ] ] ]Creé una función php recursiva que transforma esa matriz en esto:
[ ["id" => 1, "name" => "Item 1"], ["id" => 2, "name" => "Item 2"], ["id" => 3, "name" => "Item 3"], ["id" => 4, "name" => "Item 4"], ["id" => 5, "name" => "Item 5"], ]El código es este:
private array $breadcrumb = []; private function generateBreadcrumb($structure) : array { if($structure) { $this->breadcrumb[] = array( "id" => $structure['id'], "name" => $structure['name'], ); $this->generateBreadcrumb($structure['all_parents'] ?? []); } return array_reverse($this->breadcrumb); } ¿Cómo puedo rediseñar este método sin depender de la propiedad de clase $breadcrumb ?
En lugar de implementar una función recursiva, existe la posibilidad de utilizar la función integrada array_walk_recursive :
$arr = [ 'id' => 5, 'name' => 'Item 5', 'all_parents' => [ 'id' => 4, 'name' => 'Item 4', 'all_parents' => [ 'id' => 3, 'name' => 'Item 3', 'all_parents' => [ 'id' => 2, 'name' => 'Item 2', 'all_parents' => [ 'id' => 1, 'name' => 'Item 1', 'all_parents' => null ] ] ] ] ]; function generateBreadcrumb($structure): array { $retval = []; array_walk_recursive($structure, function ($item, $key) use (&$retval) { if ($key === 'id') { $retval[] = [$key => $item]; } elseif ($key === 'name') { $retval[array_key_last($retval)][$key] = $item; } }); return array_reverse($retval); } $result = generateBreadcrumb($arr);Tenga en cuenta que array_walk_recursive solo visita las hojas, por lo que, con la excepción de los 'todos_los_padres' más internos, los demás no se visitan.
Una versión no recursiva sería esta:
function generateBreadcrumb(array $arr): array { $retval = []; $temp = &$arr; do { $retval[] = [ 'id' => $temp['id'], 'name' => $temp['name'] ]; $temp = &$temp['all_parents']; } while ($temp !== null); return array_reverse($retval); }Siguiendo su código inicial, podría hacer:
function generateBreadcrumb($structure, &$output = []) : array { if ($structure) { $output[] = array( "id" => $structure['id'], "name" => $structure['name'], ); $this->generateBreadcrumb($structure['all_parents'] ?? [], $output); } return array_reverse($output); } Sin embargo, podría mejorarse, al menos evitando llamar a array_reverse() cada vez, pero solo para la llamada raíz.
Puede acumular los datos de profundidad indeterminada fusionándolos a medida que repite el árbol. No necesita introducir ninguna variable nueva para transportar los datos durante la recursividad ni necesita array_reverse() los datos devueltos.
La siguiente técnica priorizará la recursividad mientras que $structure['all_parents'] sea verdadera (no nula) y cesará la recursividad una vez que encuentre el valor null de all_parents en el subarreglo más profundo. Desde la parte inferior, se accederá a los elementos de id y name y se fusionarán en la matriz vacía o acumulada de datos de fila.
Código: ( Demostración )
class Recursing { public function generateBreadcrumb(array $structure): array { return array_merge( $structure['all_parents'] ? $this->generateBreadcrumb($structure['all_parents']) : [], [ ['id' => $structure['id'], 'name' => $structure['name']] ] ); } } $test = new Recursing; var_export($test->generateBreadcrumb($arr));Producción:
array ( 0 => array ( 'id' => 1, 'name' => 'Item 1', ), 1 => array ( 'id' => 2, 'name' => 'Item 2', ), 2 => array ( 'id' => 3, 'name' => 'Item 3', ), 3 => array ( 'id' => 4, 'name' => 'Item 4', ), 4 => array ( 'id' => 5, 'name' => 'Item 5', ), )