本文详细探讨了如何使用 php 的 `usort` 函数,在依据一个预设的参考数组对多维数组进行自定义排序时,正确处理那些未包含在参考数组中的元素。通过分析原始代码的不足,文章提出了一种健壮的解决方案,即为未匹配项分配一个极大的排序权重(如 `php_int_max`),从而确保它们被统一放置在排序结果的末尾,并提供了详细的代码示例和注意事项。
在 PHP 中,usort() 函数允许开发者使用自定义的比较函数对数组进行排序。这个比较函数(或称为回调函数)接收两个参数(通常是数组中的两个元素),并根据它们之间所需的相对顺序返回一个整数:
当我们需要根据一个预定义的顺序(例如一个字符串数组)来对另一个复杂数组进行排序时,usort() 是一个非常强大的工具。然而,一个常见的挑战是,当被排序的数组中包含一些元素,而这些元素并未出现在我们预定义的参考顺序中时,如何正确处理它们。通常,我们希望这些未匹配的元素被统一放置在排序结果的末尾。
假设我们有一个多维数组 $itemsToSort,需要根据另一个参考数组 $sortOrder 对其进行排序。$itemsToSort 中的每个子项的第一个元素($item[0])将用于与 $sortOrder 进行匹配。目标是让 $sortOrder 中定义的元素按其在 $sortOrder 中的顺序排列,而未在 $sortOrder 中定义的元素则被放置到数组的末尾。
一个常见的错误实现方式可能如下:
usort($itemsToSort, function($a, $b) use ($sortOrder){
$valA = array_search($a[0], $sortOrder);
$valB = array_search($b[0], $sortOrder);
if ($valA === false) // 如果 A 未找到
return -1; // 尝试将 A 放在 B 之前
if ($valB === false) // 如果 B 未找到(此时 A 必然已找到)
return 0; // 认为 A 和 B 顺序不变
// 如果 A 和 B 都找到,则按其在 $sortOrder 中的索引比较
if ($valA > $valB)
return 1;
if ($valA < $valB)
return -1;
return 0;
});上述代码存在逻辑缺陷:
这些逻辑错误导致当参考数组中只指定了少量值时,排序结果无法达到预期。
要解决这个问题,我们可以为那些未在 $sortOrder 中找到的元素分配一个“虚拟”的、极大的排序权重。这样,在比较时,这些具有极大权重的元素自然会排在所有具有正常(较小)权重的元素之后。PHP 提供了一个常量 PHP_INT_MAX,它代表了 PHP 能处理的最大整数值,非常适合作为这种“极大权重”的代表。
以下是使用 PHP
_INT_MAX 策略的改进 usort 回调函数:
$rankB) {
return 1; // A 排在 B 之后
}
if ($rankA < $rankB) {
return -1; // A 排在 B 之前
}
return 0; // 权重相等,保持原有相对顺序(或认为相等)
});
echo "\n排序后的数组:\n";
print_r($itemsToSort);
?>原始数组:
Array
(
[0] => Array
(
[0] => itemC
[1] => dataC
)
[1] => Array
(
[0] => itemA
[1] => dataA
)
[2] => Array
(
[0] => itemX
[1] => dataX
)
[3] => Array
(
[0] => itemB
[1] => dataB
)
[4] => Array
(
[0] => itemY
[1] => dataY
)
[5] => Array
(
[0] => itemD
[1] => dataD
)
[6] => Array
(
[0] => itemA
[1] => dataA_duplicate
)
)
排序后的数组:
Array
(
[0] => Array
(
[0] => itemA
[1] => dataA
)
[1] => Array
(
[0] => itemA
[1] => dataA_duplicate
)
[2] => Array
(
[0] => itemB
[1] => dataB
)
[3] => Array
(
[0] => itemC
[1] => dataC
)
[4] => Array
(
[0] => itemX
[1] => dataX
)
[5] => Array
(
[0] => itemY
[1] => dataY
)
[6] => Array
(
[0] => itemD
[1] => dataD
)
)从结果可以看出,itemA、itemB、itemC 按照 $sortOrder 的顺序排列,