Reputation: 5920
I have the following array:
$array = [
'z' => 2,
'd' => 1,
'a' => 2,
];
Now I would like to sort by the value (integer) and then sort by the key based on whether it is in this allowlist:
$allowlist = ['a', 'd'];
So I did the following:
arsort($array);
uksort($array, function($a, $b) {
return in_array($a, $allowlist) ? -1 : 1;
});
But that returns:
[d]: 1
[a]: 2
[z]: 2
What I really want is to sort the values first and then if there is a tie breaker, sort the key based on whether its in that allowlist which should result in this:
[a]: 2
[z]: 2
[d]: 1
Upvotes: 1
Views: 1214
Reputation: 47894
I recommend making as few evaluations as possible and as few function calls as possible.
This principle makes array_multisort()
the top choice for this task. usort()
will make more iterated calls than array_map()
(which has a linear time complexity) and that will put a drag on performance.
Flipping the whitelist array will allow it to be used as a lookup array. Because false
comes before true
when sorting in an ascending direction, check that the key is NOT found in the lookup.
$array
and param2: SORT_DESC
)Code: (Demo)
$array = [
'z' => 2,
'd' => 1,
'a' => 2,
];
$allowlist = array_flip(['a', 'd']);
array_multisort(
$array,
SORT_DESC,
array_map(fn($k) => !isset($allowlist[$k]), array_keys($array)),
$array
);
var_export($array);
Output:
array (
'a' => 2,
'z' => 2,
'd' => 1,
)
Upvotes: 1
Reputation: 31
The key is using the array_flip
function on the keys. This give the uksort
a value it can compare:
$array = [
'a' => 2,
'z' => 2,
'd' => 1,
'e' => 1,
];
$allowlist = array_flip(['a', 'e', 'd']);
uksort(
$array,
static function($a, $b) use ($array, $allowlist) {
if ($array[$a] === $array[$b]) {
if (!key_exists($a,$allowlist) || !key_exists($b,$allowlist)) {
return 0;
}
return $allowlist[$a] <=> $allowlist[$b];
}
return $array[$b] <=> $array[$a];
}
);
echo '<pre>'.print_r($array,1).'</pre>';
result in :
Array
(
[a] => 2
[z] => 2
[e] => 1
[d] => 1
)
Upvotes: 0
Reputation: 55427
You can use uksort
which will give you the keys, and you can pass the $array
into your sort function to get access to the value.
$array = [
'a' => 2,
'z' => 2,
'd' => 1
];
$allowlist = ['a', 'd'];
uksort(
$array,
static function($a, $b) use ($array, $allowlist) {
if($array[$a] === $array[$b]) {
return in_array($a, $allowlist) ? -1 : 1;
}
return $array[$b] <=> $array[$a];
}
);
This was inspired by this answer: https://stackoverflow.com/a/65315474/231316
Demo here: https://3v4l.org/toZIt
Upvotes: 2