Search code examples
phparraysalgorithmtree

How to pull children array (with object) to parent if exceed max depth?


Data

I have a data like this.

Array
(
    [0] => stdClass Object
        (
            [tid] => 1
            [t_name] => A
            [t_level] => 1
            [children] => Array
                (
                    [0] => stdClass Object
                        (
                            [tid] => 5
                            [t_name] => A.1
                            [t_level] => 2
                        )

                    [1] => stdClass Object
                        (
                            [tid] => 6
                            [t_name] => A.2
                            [t_level] => 2
                            [children] => Array
                                (
                                    [0] => stdClass Object
                                        (
                                            [tid] => 7
                                            [t_name] => A.2.1
                                            [t_level] => 3
                                            [children] => Array
                                                (
                                                    [0] => stdClass Object
                                                        (
                                                            [tid] => 9
                                                            [t_name] => A.2.1.1
                                                            [t_level] => 4
                                                            [array_val] => Array
                                                                (
                                                                    [0] => 0
                                                                    [1] => 1
                                                                    [2] => 2
                                                                )

                                                            [obj_val] => stdClass Object
                                                                (
                                                                )

                                                        )

                                                    [1] => stdClass Object
                                                        (
                                                            [tid] => 10
                                                            [t_name] => A.2.1.2
                                                            [t_level] => 4
                                                        )

                                                )

                                        )

                                    [1] => stdClass Object
                                        (
                                            [tid] => 8
                                            [t_name] => A.2.2
                                            [t_level] => 3
                                        )

                                )

                        )

                )

        )

    [1] => stdClass Object
        (
            [tid] => 2
            [t_name] => B
            [t_level] => 1
        )

    [2] => stdClass Object
        (
            [tid] => 3
            [t_name] => C
            [t_level] => 1
        )

    [3] => stdClass Object
        (
            [tid] => 4
            [t_name] => D
            [t_level] => 1
        )

)

Original data converted to JSON:

[{"tid":1,"t_name":"A","t_level":1,"children":[{"tid":5,"t_name":"A.1","t_level":2},{"tid":6,"t_name":"A.2","t_level":2,"children":[{"tid":7,"t_name":"A.2.1","t_level":3,"children":[{"tid":9,"t_name":"A.2.1.1","t_level":4,"array_val":[0,1,2],"obj_val":{}},{"tid":10,"t_name":"A.2.1.2","t_level":4}]},{"tid":8,"t_name":"A.2.2","t_level":3}]}]},{"tid":2,"t_name":"B","t_level":1},{"tid":3,"t_name":"C","t_level":1},{"tid":4,"t_name":"D","t_level":1}]

It's 4 level depth, I would like to pull all children that is deeper than 2 to their parent list at level 2.

Expected result

Array
(
    [0] => stdClass Object
        (
            [tid] => 1
            [t_name] => A
            [t_level] => 1
            [children] => Array
                (
                    [0] => stdClass Object
                        (
                            [tid] => 5
                            [t_name] => A.1
                            [t_level] => 2
                        )

                    [1] => stdClass Object
                        (
                            [tid] => 6
                            [t_name] => A.2
                            [t_level] => 2
                        )

                    [2] => stdClass Object
                        (
                            [tid] => 7
                            [t_name] => A.2.1
                            [t_level] => 2
                        )

                    [3] => stdClass Object
                        (
                            [tid] => 9
                            [t_name] => A.2.1.1
                            [t_level] => 2
                            [array_val] => Array
                                (
                                    [0] => 0
                                    [1] => 1
                                    [2] => 2
                                )

                            [obj_val] => stdClass Object
                                (
                                )

                        )

                    [4] => stdClass Object
                        (
                            [tid] => 10
                            [t_name] => A.2.1.2
                            [t_level] => 2
                        )

                    [5] => stdClass Object
                        (
                            [tid] => 8
                            [t_name] => A.2.2
                            [t_level] => 2
                        )

                )

        )

    [1] => stdClass Object
        (
            [tid] => 2
            [t_name] => B
            [t_level] => 1
        )

    [2] => stdClass Object
        (
            [tid] => 3
            [t_name] => C
            [t_level] => 1
        )

    [3] => stdClass Object
        (
            [tid] => 4
            [t_name] => D
            [t_level] => 1
        )

)

Expected result in JSON:

[{"tid":1,"t_name":"A","t_level":1,"children":[{"tid":5,"t_name":"A.1","t_level":2},{"tid":6,"t_name":"A.2","t_level":2},{"tid":7,"t_name":"A.2.1","t_level":2},{"tid":9,"t_name":"A.2.1.1","t_level":2,"array_val":[0,1,2],"obj_val":{}},{"tid":10,"t_name":"A.2.1.2","t_level":2},{"tid":8,"t_name":"A.2.2","t_level":2}]},{"tid":2,"t_name":"B","t_level":1},{"tid":3,"t_name":"C","t_level":1},{"tid":4,"t_name":"D","t_level":1}]

Code

This is what I tried but I have no idea how to move them to parent. It seems there is no function/method to set parent value or append to parent's list.

$maxDepth = 2;// max depth allowed.
$RAI = new \RecursiveArrayIterator($array);
$RII = new \RecursiveIteratorIterator($RAI, \RecursiveIteratorIterator::SELF_FIRST);
$RII->setMaxDepth($maxDepth);
foreach ($RII as $key => $value) {
    if ($key === 't_level' && intval($value) > $maxDepth) {
        // what to do? can't move them to their parent.
    }
}// endforeach;
unset($key, $value);
$array = $RII->getArrayCopy();

// see result.
print_r($array);

Solution

  • I'd suggest creating a recursive function that performs a depth-first traversal, and which relies on the $depth argument to choose whether to use the recursive result array to extend a flattened array, or to keep the nested structure.

    Code:

    function flattenAtDepth($array, $depth) {
        $result = [];
        foreach ($array as $obj) {
            $rest = [];
            if (isset($obj->children)) {
                $obj = clone $obj;
                $children = flattenAtDepth($obj->children, $depth-1);
                if ($depth <= 1) {
                    unset($obj->children);
                    $rest = $children;
                } else {
                    $obj->children = $children;
    >t_level + 1; 
                }
            }
            array_push($result, $obj, ...$rest);
        }
        return $result;
    }
    

    Call as:

    $result = flattenAtDepth($array, $maxDepth);
    

    I would not update the t_level properties as they constitute redundant information. I would actually leave those properties out.