2

SO,

The problem

I have an issue with custom array sorting. I have an array:

$rgData = [
  3 => 1,
  5 => 0,
  1 => 2,
  9 => 0,
  0 => 1,
  4 => 2,
  2 => 1,
  7 => 0,
  6 => 0,
  8 => 0,
];

-it contain keys with their counts (actually, it came after some operations with array_count_values). Now I want to sort it that:

  • Lower values comes first (i.e. usual ascending sort which can be done with asort())
  • Within one value, keys should be sorted ascending (here I need help)

For sample above result should be:

[5=>0, 6=>0, 7=>0, 8=>0, 9=>0, 0=>1, 2=>1, 3=>1, 1=>2, 4=>2]

My approach

I have no idea how to resolve this with sort by user-defined since usort or uasort accept only values for comparing while uksort - only keys and I need both of them in my comparison function. The only way that I have now - is to do this:

$rgData = ['3'=>1, '5'=>0, '1'=>2, '9'=>0, '0'=>1, '4'=>2, '2'=>1, '7'=>0, '6'=>0, '8'=>0];
$rgTemp = [];
asort($rgData);
$i     = 0;
$mPrev = current($rgData);
foreach($rgData as $mKey=>$mValue)
{
        $rgTemp[$mPrev==$mValue?$i:++$i][$mKey] = $mValue;
        $mPrev = $mValue;
}
$rgTemp = array_map(function($rgX)
{
        ksort($rgX);
        return $rgX;
}, $rgTemp);
$rgData = [];
//can't use call_user_func_array('array_merge', $rgTemp) - it spoils numeric keys
foreach($rgTemp as $rgX)
{
        foreach($rgX as $mKey=>$mValue)
        {
                $rgData[$mKey] = $mValue;
        }
}
//var_dump($rgData);

-i.e. split array by values first, then do the stuff.

The question

How to do this in more easy way? I used asort + cycled ksort via array_map with final gathering loop. Temp array also used. It looks weird. I hope simpler method exists.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Alma Do
  • 37,009
  • 9
  • 76
  • 105
  • Most concisely from PHP7.4 and up: [`uksort($rgData, fn($a, $b) => [$rgData[$a], $a] <=> [$rgData[$a], $a]);`](https://stackoverflow.com/a/65315474/2943403) – mickmackusa Sep 04 '22 at 22:02

1 Answers1

2

This should do what you want. It sorts by the keys, but we can access the value inside the function, so we can use that as our sort criteria too.

uksort($rgData, function($a, $b) use($rgData){
    $aVal = $rgData[$a];
    $bVal = $rgData[$b];

    // Compare the values
    if($aVal === $bVal){
        // If they are the same, compare the keys
        return $a - $b;
    }
    // Otherwise compare the values
    return $aVal - $bVal;
});
gen_Eric
  • 223,194
  • 41
  • 299
  • 337