GrayRF GrayRF - 1 month ago 4
PHP Question

Sorting racers / players by places

I have data about some racers presented in the following form:


array(
array(name => "the first racer", places => [1,3,1,5,6,2,6,7,8]),
array(name => "the second racer", places => [2,4,2,5,7])
...
)


Advise the best way to sort them so that the first racers were who have better places. For example If the first racer has at least one first place and the other not, the first is higher in the list. If they both have the first places, compare the number of first place. If the number equal too, compare the second places and so on.

My solution (It does not look very elegant. Maybe it can be done somehow easier):



$racers = array(
array('name' => "the first racer", 'places' => [1,3,1,5,6,2,6,7,8,9]),
array('name' => "the second racer", 'places' => [1,3,1,5,6,2,6,7,8]),
array('name' => "the third racer", 'places' => [2,3,2,5,7,10]),
array('name' => "the fourth racer", 'places' => [2,3,10,6,6,10]),
array('name' => "the fifth", 'places' => [2,3,2,5,7,10,1]),
);

usort($racers, function($prev, $next) {
// order places for every racer
sort($prev['places']);
sort($next['places']);

//compare each place with each other
foreach ($prev['places'] AS $key => $prevRacerPlace) {
// if all values are equal, we compare the number of races
if (!isset($next['places'][$key])) {
return -1;
}
$nextRacerPlace = $next['places'][$key];
$diff = $prevRacerPlace - $nextRacerPlace;
if ($diff !== 0) {
return $diff;
}
}
// if all values are equal, we compare the number of races
if (count($next['places']) > count($prev['places'])) {
return 1;
}
});

var_dump($racers);


Answer

It would be a quite nice to make some preparations before custom sorting. So we avoid a nested sorts in lambda function:

foreach ($racers as $index => $racer) {
    $racers[$index]['sorted_places'] = $racer['places'];
    sort($racers[$index]['sorted_places']);
}

In sorting lambda function we compare a heads of prepared sorted places and return a first defined value. If racer B top place result better than A, return 1. If racer A top place result better than B, return -1. On equal results continue checks for next top places.

usort($racers, function ($a, $b) {
    unset($value);
    do {
        $topA = array_shift($a['sorted_places']);
        $topB = array_shift($b['sorted_places']);

        if (is_null($topA) && is_null($topB)) {
            $value = 0;
        } elseif (is_null($topA)) {
            $value = 1;
        } elseif (is_null($topB)) {
            $value = -1;
        } elseif ($topA > $topB) {
            $value = 1;
        } elseif ($topA < $topB) {
            $value = -1;
        }
    } while (!isset($value));
    return $value;
});