Home > OS >  Use recursion to accumulate rows without depending on class property variable
Use recursion to accumulate rows without depending on class property variable

Time:02-27

Having this array :

[
    "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
                ]
            ]
        ]
    ]
]

I created a recursive php function that transform that array to this:

[
    ["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"],
]

The code is this:

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

How can I redesign this method without depending on class property $breadcrumb?

CodePudding user response:

By following your initial code, you could do:

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

However it could be improved, at least by avoiding to call array_reverse() each time, but only for the root call.

CodePudding user response:

Instead of implementing a recursive function there is the possibility of using the built-in array_walk_recursive function:

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

Note that array_walk_recursive only visits leafs, so with the exception of the innermost 'all_parents', the other ones are not visited.

A none-recursive version would be this:

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

CodePudding user response:

You can accumulate the indeterminate-depth data by merging as you recurse the tree. You do not need to introduce any new variables to carry the data while recursing nor do you need to array_reverse() the returned data.

The below technique will prioritize recursion while $structure['all_parents'] is truthy (not null) and cease recursion once it encounters the null all_parents value in the deepest subarray. From the bottom, the id and name elements will be accessed and merged into the empty or accumulated array of row data.

Code: (Demo)

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));

Output:

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',
  ),
)
  • Related