更高效的解决方案
$dict = array_flip($order);
$positions = array_map(function ($elem) use ($dict) { return $dict[$elem['id']] ?? INF; }, $array);
array_multisort($positions, $array);
不要在每次比较时重新计算位置
当您的数组很大或获取 id 成本较高时,使用usort()
可能会变得很糟糕,因为您会为每次比较重新计算 id。尝试array_multisort()
使用预先计算的位置(参见mediumsort
或fastsort
在下面的示例中),这并不复杂。
同样在每次比较时在 order 数组中搜索 id (如在接受的答案中)也不会提高性能,因为每次比较都会对其进行迭代。计算一次。
在下面的代码片段中,您可以看到主要的三个排序函数:
slowsort
接受的答案。搜索每次比较的位置。
mediumsort
slowsort
通过提前计算位置来
改进
fastsort
mediumsort
通过避免完全搜索来
改进。
请注意,这些句柄元素的 id 未按顺序给出,方法是提供INF
. 如果您的订单数组与原始数组的 id 1 对 1 匹配,那么避免一起排序,只在正确的位置插入元素是要走的路。我添加了一个功能cheatsort
正是这样做的。
您可以更一般地按权重对数组进行排序(参见weightedsort
示例)。确保只计算一次权重,以获得良好的性能。
性能(对于长度为 1000 的数组)
fastsort about 1 ms
mediumsort about 3 ms
slowsort about 60 ms
提示:对于更大的数组,差异会变得更糟。
排序功能比较
<?php
/**
* accepted answer
*
* re-evaluate position in order on each comparison
*/
function slowsort(&$array, $order, $key = 'id')
{
usort($array, function ($a, $b) use ($order, $key) {
$pos_a = array_search($a[$key], $order);
$pos_b = array_search($b[$key], $order);
return $pos_a - $pos_b;
});
}
/**
* calculate element positions once
*/
function mediumsort(&$array, $order, $key = 'id')
{
$positions = array_map(function ($elem) use ($order, $key) {
return array_search($elem[$key], $order);
}, $array);
array_multisort($positions, $array);
}
/**
* calculate positions without searching
*/
function fastsort(&$array, $order, $key = 'id')
{
$dict = array_flip($order);
$positions = array_map(function ($elem) use ($dict, $key) {
return $dict[$elem[$key]] ?? INF;
}, $array);
array_multisort($positions, $array);
}
/**
* when each order element gets used exactly once, insert elements directly
*/
function cheatsort(&$array, $order, $key = 'id')
{
$dict = array_flip($order);
$copy = $array;
foreach ($copy as $elem) {
$pos = $dict[$elem[$key]];
$array[$pos] = $elem;
}
}
/**
* Sort elements in $array by their weight given by $weight_func
*
* You could rewrite fastsort and mediumsort by replacing $position by a weight function
*/
function weightedsort(&$array, $weight_func)
{
$weights = array_map($weight_func, $array);
array_multisort($weights, $array);
}
/**
* MEASUREMENTS
*/
/**
* Generate the sorting problem
*/
function generate($size = 1000)
{
$order = array();
$array = array();
for ($i = 0; $i < $size; $i++) {
$id = random_int(0, PHP_INT_MAX);
$order[] = $id;
$array[] = array('id' => $id);
}
shuffle($order);
return [$array, $order];
}
/**
* Time $callable in ms
*/
function time_it($callable)
{
$then = microtime(true);
$callable();
$now = microtime(true);
return 1000 * ($now - $then);
}
/**
* Time a sort function with name $sort_func
*/
function time_sort($sort_func)
{
echo "Timing $sort_func", PHP_EOL;
[$array, $order] = generate();
echo time_it(function () use ($sort_func, &$array, $order) {
$sort_func($array, $order);
}) . ' ms' . PHP_EOL;
}
time_sort('cheatsort');
time_sort('fastsort');
time_sort('mediumsort');
time_sort('slowsort');