Reputation: 3214
Is there a way to sort a PHP array by return value of a custom function? Like Python's sorted(arr, key=keyfn)
or Clojure's (sort-by keyfn arr)
,
usort($array, function ($a, $b) {
$key_a = keyfn($a);
$key_b = keyfn($b);
if ($key_a == $key_b) return 0;
if ($key_a < $key_b) return -1;
return 1;
});
The above does what I want, but it's verbose and it calls keyfn
(which can be slow) much more often than needed. Is there a more performant approach?
Upvotes: 1
Views: 152
Reputation: 47934
From PHP7.4 and higher you can modernize/refine @u_mulder's snippet using the null coalescing assignment operator as the following.
Code: (Demo)
usort($array, function ($a, $b) {
static $cache;
return ($cache[$a] ??= keyfn($a)) <=> ($cache[$b] ??= keyfn($b));
});
Alternatively, making mapped calls of your custom function will be more concise, but will result in a greater number of custom function calls. (Demo)
array_multisort(array_map('keyfn', $array), $array);
This can be prevented in a classic loop, again with the null coalescing operator, but it may be a little harder to conceptualize with the double-assignment line. (Demo)
$new = [];
foreach ($array as $v) {
$new[] = $cache[$v] ??= keyfn($v);
}
array_multisort($new, $array);
Upvotes: 0
Reputation: 3214
I ended up implementing it this way:
function sort_by($key_f, $arr) {
$values = array_map($key_f, $arr);
asort($values);
$sorted_arr = array();
foreach ($values as $idx => $value) {
$sorted_arr[] = $arr[$idx];
}
return $sorted_arr;
}
Upvotes: 2
Reputation: 54841
Some simple code for caching results of predicate
and sorting (using spaceship
operator which reduces lines where you return 0,1,-1). In case of predicate
result as int
you can even return $key_a - $key_b
:
$array = [2,2,2,1,1,0,0,8];
$values_cache = [];
usort($array, function ($a, $b) use (&$values_cache) {
$key_a = isset($values_cache[$a]) ? $values_cache[$a] : ($values_cache[$a] = keyfn($a));
$key_b = isset($values_cache[$b]) ? $values_cache[$b] : ($values_cache[$b] = keyfn($b));
return $key_a <=> $key_b;
});
echo '<pre>', print_r($array), '</pre>';
function keyfn($v) {
echo 'call a keyfn' . PHP_EOL;
return 2 * $v;
}
Simple fiddle https://3v4l.org/W1N7Y
Upvotes: 1