20

I am having a hard time trying recursively sort a multidimensional array on its keys. I tried with usort(), but with no success.

Sample data:

[
    'first_level' => [
        'dir_3' => [
            'subdir_1' => [
                'file_2.mp4' => (object) [
                    'name' => 'file_2.mp4',
                ],
                'file_1.mp4' => (object) [
                    'name' => 'file_1.mp4',
                ],
            ],
        ],
        'dir_1' => [
            'subdir_2' => [
                'file_6.mp4' => (object) [
                    'name' => 'file_6.mp4',
                ],
                'file_9.mp4' => (object) [
                    'name' => 'file_9.mp4',
                ],
                'file_7.mp4' => (object) [
                    'name' => 'file_7.mp4',
                ],
            ],
            'subdir_1' => [
                'file_8.mp4' => (object) [
                    'name' => 'file_8.mp4',
                ],
            ],
        ],
    ],
]

Desired result:

[
    'first_level' => [
        'dir_1' => [
            'subdir_1' => [
                'file_8.mp4' => (object) [
                    'name' => 'file_8.mp4',
                ],
            ],
            'subdir_2' => [
                'file_6.mp4' => (object) [
                    'name' => 'file_6.mp4',
                ],
                'file_7.mp4' => (object) [
                    'name' => 'file_7.mp4',
                ],
                'file_9.mp4' => (object) [
                    'name' => 'file_9.mp4',
                ],
            ],
        ],
        'dir_3' => [
            'subdir_1' => [
                'file_1.mp4' => (object) [
                    'name' => 'file_1.mp4',
                ],
                'file_2.mp4' => (object) [
                    'name' => 'file_2.mp4',
                ],
            ],
        ],
    ],
]
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
4v4l0n42
  • 313
  • 1
  • 2
  • 6

4 Answers4

38

Use a recursive function to call ksort on the current level and all deeper subarrays.

function recur_ksort(&$array) {
    foreach ($array as &$value) {
        if (is_array($value))
            recur_ksort($value);
     }
     ksort($array);
}

recur_ksort($array);
var_export($array);

Demo: https://3v4l.org/Xede5

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
netcoder
  • 66,435
  • 19
  • 125
  • 142
  • 2
    This answer made me spot a silly mistake in my own code. :o I came up with some very similar code on my own, but I forgot the `&` before `&$value`, so I was breaking my head why only my first level was getting sorted. Thanks for unwittingly helping me. ;) – Byson Dec 04 '14 at 13:05
5

You need to use ksort with recursion. Demo

function recursive_ksort(&$array) {
    foreach ($array as &$v) {
        if (is_array($v)) {
            recursive_ksort($v);
        }
    }
    ksort($array);
}

recursive_ksort($array);
var_export($array);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Vincent Mimoun-Prat
  • 28,208
  • 16
  • 81
  • 124
4
function ksort_recursive(&$array)
{
    if (is_array($array)) {
        ksort($array);
        array_walk($array, 'ksort_recursive');
    }
}

It is not necessary within the recursive function to return ksort() -- this would return the unwanted success boolean value from ksort() anyhow.

Note that this function does not throw "Warning: ksort() expects parameter 1 to be array" when given a non-array - this matches my requirements but perhaps not yours. Demo: https://3v4l.org/bogAU

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Deebster
  • 2,829
  • 1
  • 26
  • 26
0

It is fair to assume that you'd like your data ti be sorted "naturally" -- meaning that the number portion of the directory and file names should be sorted numerically instead of as simple strings. Without sorting naturally, dir_10 will be moved in front of dir_2 because when comparing the 5th character of the two strings, 1 is less than 2.

Code: (Demo)

function nat_ksort_r(&$data): void
{
    if (is_array($data)) {
        ksort($data, SORT_NATURAL);
        array_walk($data, __METHOD__);
    }
}

nat_ksort_r($array);
var_export($array);
  • For natural sorting, apply the SORT_NATURAL flag to the ksort() call.
  • For simpler maintenance of the recursive function, recall the function using the __METHOD__ magic constant. This makes one less place to change nat_ksort_r() if you wish to call the custom function something else.
  • This recursive function does not return any data; it modifies the original array by reference.
  • The lowest level contains objects and this data does not get sorted by the function.

The above function can also use a classic loop instead of a functional iterator. (Demo)

function nat_ksort_r(&$data): void
{
    if (is_array($data)) {
        ksort($data, SORT_NATURAL);
        foreach ($data as &$item) {
            (__METHOD__)($item);
        }
    }
}

nat_ksort_r($array);
var_export($array);

You can even write the code in a completely anonymous fashion. Demo

$nat_ksort_r = function(&$data) use (&$nat_ksort_r) {
    if (is_array($data)) {
        ksort($data, SORT_NATURAL);
        foreach ($data as &$item) {
            $nat_ksort_r($item);
        }
    }
};
$nat_ksort_r($array);
var_export($array);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136