-2

I have a multidimensional array in this structure:

$arr = [
    0 => ['ref' => 'Q1'],
    1 => ['ref' => 'C6'],
    2 => ['ref' => 'C13'],
    3 => ['ref' => 'S3'],
    4 => ['ref' => 'Q11'],
    8 => ['ref' => 'S7'],
    9 => ['ref' => 'C4'],
];

I want to sort the array so that the order of values are S, Q, D, C, P, E and if possible have each alphanumeric ascending while also keeping the associate key [ref], like this:

$arr = [
    0 => ['ref' => 'S3'],
    1 => ['ref' => 'S7'],
    2 => ['ref' => 'Q1'],
    3 => ['ref' => 'Q11'],
    4 => ['ref' => 'C4'],
    8 => ['ref' => 'C6'],
    9 => ['ref' => 'C13'],
];
4
  • Are you missing keys 5, 6 and 7 on purpose, or was that a mistake? Commented Aug 16, 2018 at 8:26
  • I'm missing them on purpose. Commented Aug 16, 2018 at 8:30
  • usort and uasort both accept a user-defined comparison function, so they deal with whatever you tell them to deal with. Commented Aug 16, 2018 at 8:32
  • @iainn I'll change my incorrect statement, thank you Commented Aug 16, 2018 at 8:40

2 Answers 2

1

This code should do what you want. It uses usort with a compare function that first looks at the leading character and sorts that based on your specified order: S, Q, D, C, P, E. If the character is the same, it then compares the integer part of the value. Now the problem is that this loses the keys. We can't use uasort to get around that, as it will move the keys with the corresponding value. To get around that we first grab the keys of the array, then combine the keys with the sorted array at the end:

function mysort($a, $b) {
    $sort_order = ['S', 'Q', 'D', 'C', 'P', 'E'];
    // first, sort by leading character
    $s = array_search($a['ref'][0], $sort_order) - array_search($b['ref'][0], $sort_order);
    if ($s != 0) return $s;
    // if the same, sort by numeric part
    return (int)substr($a['ref'], 1) - (int)substr($b['ref'], 1);
}
$keys = array_keys($arr);
usort($arr, 'mysort');
$arr = array_combine($keys, $arr);

Output:

Array
(
    [0] => Array
        (
            [ref] => S3
        )
    [1] => Array
        (
            [ref] => S7
        )
    [2] => Array
        (
            [ref] => Q1
        )
    [3] => Array
        (
            [ref] => Q11
        )
    [4] => Array
        (
            [ref] => C4
        )
    [8] => Array
        (
            [ref] => C6
        )
    [9] => Array
        (
            [ref] => C13
        )
)
Sign up to request clarification or add additional context in comments.

1 Comment

Mine didn't use natural sorting like yours did, so it (probably) gives a wrong answer, unless that's how the OP wants it. And using array_combine is much better than creating an array of keys and looping over it, and creating a duplicate array. I actually wasn't aware of that function.
0

For best performance, do not perform data preparations inside of the sorting algorithm. Calling array_search() inside of the sorting algorithm is not best practice.

array_multisort() Demo

$map = array_flip(
    ['S', 'Q', 'D', 'C', 'P', 'E']
);

foreach ($array as $keys[] => ['ref' => $v]) {
    sscanf($v, '%1s%d', $letter, $integers[]);
    $priorities[] = $map[$letter];
}
array_multisort(
    $priorities,
    $integers,
    $array
);
var_export(array_combine($keys, $array));

The less efficient version of above (because it calls sscanf() twice per iteration) using usort() looks like this: Demo

$map = array_flip(
    ['S', 'Q', 'D', 'C', 'P', 'E']
);

$keys = array_keys($array);

usort(
    $array,
    fn($a, $b) => [sscanf($a['ref'], '%1s%d', $letter, $integer), $map[$letter], $integer]
                  <=>
                  [sscanf($b['ref'], '%1s%d', $letter, $integer), $map[$letter], $integer]
);
var_export($array);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.