phpsortingmultidimensional-arrayusortasort

Properly sorting multidimensional array using usort and spaceship operator


I need to sort the following array with following logic: If the 'score' is the same, I want to compare using 'time'. The array is as follows:

$user_scores = [ 82 => [ 'score' => 1, 'time' => 6.442 ],
                 34 => [ 'score' => 1, 'time' => 5.646 ],
                 66 => [ 'score' => 3, 'time' => 1.554 ]
               ]

The 'keys' in the above array are the 'user_ids', which I need to preserve in the sorted array. My best attempt so far is as below -

$result = usort( $user_scores, function ( $a, $b ) {
    if ( $a['score'] == $b['score'] ) {
        return $a['time'] == $b['time'];
        }
    return $a['score'] <=> $b['score'];
} );

Obviously, this isn't working, and I'm getting the $user_scores array with all keys replaced with 0, 1, 2 instead of user_ids ( 82, 34 and 66 ). The sorting ain't working either.

For the above array, my desired output would be $user_scores array :

$user_scores = [ 34 => [ 'score' => 1, 'time' => 5.646 ],
                 82 => [ 'score' => 1, 'time' => 6.442 ],
                 66 => [ 'score' => 3, 'time' => 1.554 ]
               ]

Would really appreciate if you could tell me how to make this work using the spaceship operator (if it makes sense). Thank you for your time and I look forward to your responses.

---UPDATE ---

The sorting logic required is like this:

  1. Higher the score, higher would be the rank.
  2. Higher the time, lower would be the rank.

It's basically sorting the results of quiz. The top scorers with the least amount of time would be at the top; and those with lower score and higher time would be at the bottom.


Solution

  • To preserve the keys, you just need to use uasort().

    I recommend the spaceship operator to make the 3-way comparisons (available from PHP7 and higher).

    $result in your code is only going to return true/false so that is no use to you. The sort()'ing functions aren't to be assigned to a variable; they directly modify the input array.

    When offering array data to the spaceship operator, the first two elements will be compared. If there is a difference, then 1 or -1 will be returned. If there is a tie (comparison evaluates as 0, then the next two elements will be evaluated. This behavior continues until a non-zero evaluation occurs or there are no more elements to iterate. This is a rather clean/readable syntax to implement.

    To sort in a descending direction, write the $b value on the left and the $a value on the right.

    Code: (Demo)

    $user_scores = [
        82 => ['score' => 1, 'time' => 6.442],
        34 => ['score' => 1, 'time' => 5.646],
        66 => ['score' => 3, 'time' => 1.554],
        7  => ['score' => 2, 'time' => 4.442],
        99 => ['score' => 4, 'time' => 3.646],
        55 => ['score' => 1, 'time' => 2.554]
    ];
    
    uasort($user_scores, function($a, $b) {
        return [$b['score'], $a['time']] <=> [$a['score'], $b['time']];
    });
    var_export($user_scores);
    

    Output:

    array (
      99 => 
      array (
        'score' => 4,
        'time' => 3.646,
      ),
      66 => 
      array (
        'score' => 3,
        'time' => 1.554,
      ),
      7 => 
      array (
        'score' => 2,
        'time' => 4.442,
      ),
      55 => 
      array (
        'score' => 1,
        'time' => 2.554,
      ),
      34 => 
      array (
        'score' => 1,
        'time' => 5.646,
      ),
      82 => 
      array (
        'score' => 1,
        'time' => 6.442,
      ),
    )