phparraysrecursionmultidimensional-arrayusort

Recursively sort levels within a multidimensional array of unknown depth by a column while preserving keys


To sort an existing multidimensional array I tried to use usort, but it wont bring me any result. As I can not use nested sets or something like this because of the given structure, I have to sort it:

My array:

[
    2 => [
        'position' => 0,
        'children' => [
            3 => ['position' => "375"],
            5 => ['position' => "44"],
            6 => ['position' => "567"],
            9 => [
                'position' =>  "12",
                'children' => [
                    74 => ['position' => "2"],
                    76 => ['position' => "3"],
                    77 => ['position' => "1"],
                ]
            ],
            62 => ['position' => "34"],
            63 => ['position' => "11"],
            66 => ['position' => "114"],
            74 => ['position' => "912"],
            76 => ['position' => "4564"],
        ]
    ]
]

I would like to sort every level by its node named "children". Recursion should be no problem. It can be a variable structure where the node "children" can exist ... or not :)

Desired result after sorting by position column values:

[
    2 => [
        'position' => 0,
        'children' => [
            63 => ['position' => "11"],
            9 => [
                'position' =>  "12",
                'children' => [
                    77 => ['position' => "1"],
                    74 => ['position' => "2"],
                    76 => ['position' => "3"],
                ]
            ],
            62 => ['position' => "34"],
            5 => ['position' => "44"],
            66 => ['position' => "114"],
            3 => ['position' => "375"],
            6 => ['position' => "567"],
            74 => ['position' => "912"],
            76 => ['position' => "4564"],
        ]
    ]
]

I tried the following code unsuccessfully:

public function recur($data){
        // ...
        foreach($data as $key=>$value){
            if (array_key_exists('children', $value)) {
            usort($value, function ($a, $b): int {
                if ($a['position'] === $b['position']) {
                // ....
                }
                return $a['position'] <=> $b['position'];
            });
        }
    }
}  

Solution

  • Your code does not return any data, so we must assume that your intention is to modify by reference. To implement modification by reference in your recursive method, use the & symbol in the method's argument signature and the $value variable of the foreach().

    The key-preserving sort on the position column data needs to be done outside of the loop to access the data properly.

    After sorting the current row, you can recursively iterate any subset that contains children data.

    Code: (Demo)

    function recur(&$data): void
    {
        uasort($data, fn($a, $b) => $a['position'] <=> $b['position']);
        foreach($data as &$value){
            if (isset($value['children'])) {
                recur($value['children']);
            }
        }
    }
    recur($array);
    var_export($array);