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

58 Views Asked by At

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'];
            });
        }
    }
}  
1

There are 1 best solutions below

2
mickmackusa On BEST ANSWER

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