-1

I have a multidimensional array with parent-child relationships and I want to flatten its data into a 2d array of the scalar data from each level using recursion.

[
    'children' => [
        [
            'id' => 123,
            'title' => 'test',
            'children' => [
                [
                    'id' => 345,
                    'title' => 'test 1',
                    'children' => [],
                ],
                [
                    'id' => 567,
                    'title' => 'test 2',
                    'children' => [
                        [
                            'id' => 789,
                            'title' => 'test 3',
                            'children' => [],
                        ],
                        [
                            'id' => 333,
                            'title' => 'tset 4',
                            'children' => [
                                [
                                    'id' => 222,
                                    'title' => 'test 5',
                                    'children' => [],
                                ],
                                [
                                    'id' => 111,
                                    'title' => 'test 55',
                                    'children' => [],
                                ],
                                [
                                    'id' => 444,
                                    'title' => 'test 556',
                                    'children' => [],
                                ],
                                [
                                    'id' => 666,
                                    'title' => 'test44',
                                    'children' => [],
                                ],
                            ],
                        ],
                        [
                            'id' => '5556',
                            'identifier' => 'test 2',
                            'children' => [
                                [
                                    'id' => 888,
                                    'title' => 'test 2',
                                    'children' => [],
                                ],
                                [
                                    'id' => 255,
                                    'title' => 'tset',
                                    'children' => [],
                                ],
                                [
                                    'id' => 454,
                                    'title' => 'warm-up-5837',
                                    'children' => [],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
];

My current code:

function flattenTree(array $elements, $i, $branch)
{
    foreach($elements as $element) {
        $branch[$i++] = [
            'id' => $element['id'],
            'title' => $element['title']
        ];
        if (!empty($element['children'])) {
               flattenTree($element['children'], $i, $branch);
        }
    }

    return $branch;
}

Desired output:

Array
(
    [0] => [
        [id] => 123
        [title] => 'test'
    ]
    [1] => [
        [id] => 345
        [title] => 'test 1'
    ]
    [2] => [
        [id] => 567
        [title] => 'test 2'
    ]
    [3] => [
        [id] => 789
        [title] => 'test 3' 
    ]
    [4] => [
        [id] => 333
        [title] => 'tset 4'
    ]
    [5] => [
        [id] => 222
        [title] => 'test 5'
    ]
    [6] => [
        [id] => 111
        [title] => 'test 55'
    ]
    [7] => [
        [id] => 444
        [title] => 'test 556'
    ]
    [8] => [
        [id] => 666
        [title] => 'test44'
    ]
    [9] => [
        [id] => '5556'
        [identifier] => 'test 2'
    ]
    [10] => [
        [id] => 888
        [title] => 'test 2'
    ]   
    [11] => [
        [id] => 255
        [title] => 'tset'
    ]
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Bhumi Shah
  • 9,323
  • 7
  • 63
  • 104
  • 5
    It would be nice if you showed the data as from a `var_export($theArray)` so we could copy/paste the data easily to test an answer – RiggsFolly Apr 27 '23 at 11:20
  • 1
    You're not doing anything with the return value of `$this->flattenTree($element['children'], $i, $branch)`… – deceze Apr 27 '23 at 11:20
  • You are also using `$this` but you have not shown the method as a method, but just as a function? Do we assume it is really in a class or that `$this` is an error – RiggsFolly Apr 27 '23 at 11:34
  • @RiggsFolly removed $this – Bhumi Shah Apr 27 '23 at 11:36
  • And the `exit;` statement will of course also prevent this from passing the gathered data back up to the next level caller. – CBroe Apr 27 '23 at 11:39
  • 1
    What have you tried to resolve the problem? Where are you stuck? What's the expected output for this? – Nico Haase Apr 27 '23 at 11:50
  • 1
    It doesn't seem like `$i` serves any purpose here, remove it. Then all you need is `$branch = flattenTree($element['children'], $branch)`. – deceze Apr 27 '23 at 12:13
  • @BhumiShah Can you share an online link for this? – nice_dev Apr 27 '23 at 13:21

2 Answers2

0

Ok, a couple of observations.

  • You are using $i. This pointer needs to be passed by reference in every subsequent method call, else the previous $i location will be overridden when the recursive call comes back.

  • You are adding results to $branch. Now, you would again wish to pass this as a reference to affect the same output variable.

  • return $branch; This is entirely not needed if you passing the output variable pass by reference.

Snippet:

<?php

function flattenTree(array $elements, &$i, &$branch){
    foreach($elements as $element) {
        if(isset($element['id'], $element['title'])){ // you can remove this if condition if you find it redundant and if your array is symmetric
            $branch[$i++] = [
                'id' => $element['id'],
                'title' => $element['title']
            ];
        }
        
        if (!empty($element['children'] ?? [])) {
            flattenTree($element['children'], $i, $branch);
        }
    }
}


$output = [];

$i = 0;

flattenTree($your_array, $i, $output);

print_r($output);
nice_dev
  • 17,053
  • 2
  • 21
  • 35
0

Your coding attempt looks more complicated than necessary.

  1. Iterate the children element of whatever array is passed into the recursive function.
  2. Unconditionally cache the id and title/identifier data.
  3. If the current child/row has a populated children element, then make the recursive call and push its returned payload as individual elements into the parent's cache.
  4. Each level will pass its payload up. On the top level, the whole flattened result will be returned.

There is no need to maintain a counter.

Code: (Demo)

function flattenTree(array $tree): array
{
    $flat = [];
    foreach ($tree['children'] ?? [] as $child) {
        $flat[] = ['id' => $child['id'], 'title' => $child['title'] ?? $child['identifier']];
        if (!empty($child['children'])) {
           array_push($flat, ...flattenTree($child, $flat));
        }
    }
    return $flat;
}
var_export(flattenTree($array));

If you want to keep that identifier key in the output, then you can merely filter the $child data with is_scalar() before caching. (Demo)

function flattenTree(array $tree): array
{
    $flat = [];
    foreach ($tree['children'] ?? [] as $child) {
        $flat[] = array_filter($child, 'is_scalar');        #<--- HERE
        if (!empty($child['children'])) {
           array_push($flat, ...flattenTree($child, $flat));
        }
    }
    return $flat;
}
var_export(flattenTree($array));
mickmackusa
  • 43,625
  • 12
  • 83
  • 136