
本文介绍一种分步策略:先分离带锁行与非锁行,对非锁行按 score 和 strtotime 降序排序,再将带锁行按其 lock 值作为索引原位插入,从而实现“局部排序、全局位置锁定”的效果。
本文介绍一种分步策略:先分离带锁行与非锁行,对非锁行按 score 和 strtotime 降序排序,再将带锁行按其 lock 值作为索引原位插入,从而实现“局部排序、全局位置锁定”的效果。
在实际业务中(如内容推荐列表、带置顶项的排行榜),常需对数据进行多字段排序,但同时要求某些“置顶”或“锁定”项严格保留在原始位置——例如 lock > 0 的行应视为 sticky 行,其 lock 值即为期望保留的绝对索引位置(而非优先级数值)。直接使用 usort() 无法满足该需求,因为比较器函数仅能访问当前比对的两个元素,无法感知全局索引或原始位置。
正确的解法是采用 三阶段置换法:
-
分离:遍历原始数组,将
lock > 0的行提取至$locked数组,其余存入$unlocked; -
排序:对
$unlocked执行双字段降序排序(先score降序,score相同时strtotime降序); -
归位:按
lock值(即目标索引)将每个锁定行插入$unlocked对应位置,使用array_splice()精确注入。
✅ 注意:
lock值必须为有效非负整数索引(如示例中lock=3表示插入到索引 3 处,即第 4 个位置),且多个锁定行的lock值不能冲突(否则后插入者会挤占前者位置)。若原始数据中lock非严格递增,需先按lock升序排序$locked,确保插入顺序由小到大,避免索引偏移。
以下是完整可运行的 PHP 实现:
function sortWithLockedRows(array $input): array
{
$unlocked = [];
$locked = [];
// Step 1: 分离锁定行与非锁定行
foreach ($input as $item) {
if (isset($item['lock']) && $item['lock'] > 0) {
$locked[] = $item;
} else {
$unlocked[] = $item;
}
}
// Step 2: 对非锁定行按 score(降序)、strtotime(降序)排序
usort($unlocked, function ($a, $b) {
if ($a['score'] $b['score'] === 0) {
return $b['strtotime'] $a['strtotime']; // strtotime 降序
}
return $b['score'] $a['score']; // score 降序
});
// Step 3: 按 lock 值升序排列锁定行,确保插入时不互相干扰
usort($locked, fn($a, $b) => $a['lock'] $b['lock']);
// 逐个插入锁定行(lock 值即目标索引)
foreach ($locked as $item) {
$pos = (int)$item['lock'];
// 安全校验:防止越界(可选)
if ($pos >= 0 && $pos 7867867, 'lock' => 0, 'score' => 322, 'strtotime' => 16614713],
['id' => 7867867, 'lock' => 0, 'score' => 444, 'strtotime' => 16614613],
['id' => 7867867, 'lock' => 3, 'score' => 0, 'strtotime' => 16613713],
['id' => 7867867, 'lock' => 0, 'score' => 11, 'strtotime' => 16612713],
['id' => 7867867, 'lock' => 5, 'score' => 0, 'strtotime' => 16614413],
['id' => 7867867, 'lock' => 0, 'score' => 42, 'strtotime' => 16614113],
['id' => 7867867, 'lock' => 0, 'score' => 22, 'strtotime' => 16614013],
];
$result = sortWithLockedRows($data);
print_r($result);
关键要点总结:
-
lock字段在此方案中承担双重角色:既是“是否锁定”的标志,也是目标插入索引,设计时需确保语义清晰; -
array_splice($array, $offset, 0, $item)是实现“原位插入”的核心,$offset必须为整数且在合法范围内; - 若业务中
lock表示优先级(如 1=最高)而非索引,需预先映射为位置索引,或改用其他稳定排序策略(如装饰-排序-还原); - 该方法时间复杂度为 O(n log n),空间复杂度 O(n),适用于中等规模数据;超大规模场景可考虑就地分区优化。
通过这种结构化、可验证的三步法,你既能享受标准排序的简洁性,又能精准控制关键行的布局稳定性。










