skip to Main Content

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'
    ]
)

2

Answers


  1. 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);
    
    Login or Signup to reply.
  2. 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));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search