
本文详细探讨了如何使用 php 的 `usort` 函数,在依据一个预设的参考数组对多维数组进行自定义排序时,正确处理那些未包含在参考数组中的元素。通过分析原始代码的不足,文章提出了一种健壮的解决方案,即为未匹配项分配一个极大的排序权重(如 `php_int_max`),从而确保它们被统一放置在排序结果的末尾,并提供了详细的代码示例和注意事项。
理解 usort 与自定义排序
在 PHP 中,usort() 函数允许开发者使用自定义的比较函数对数组进行排序。这个比较函数(或称为回调函数)接收两个参数(通常是数组中的两个元素),并根据它们之间所需的相对顺序返回一个整数:
如果第一个参数应排在第二个参数之前,返回负数(例如 -1)。如果第一个参数应排在第二个参数之后,返回正数(例如 1)。如果两个参数的顺序无关紧要(或被认为是相等),返回 0。当我们需要根据一个预定义的顺序(例如一个字符串数组)来对另一个复杂数组进行排序时,usort() 是一个非常强大的工具。然而,一个常见的挑战是,当被排序的数组中包含一些元素,而这些元素并未出现在我们预定义的参考顺序中时,如何正确处理它们。通常,我们希望这些未匹配的元素被统一放置在排序结果的末尾。
原始问题分析
假设我们有一个多维数组 $itemsToSort,需要根据另一个参考数组 $sortOrder 对其进行排序。$itemsToSort 中的每个子项的第一个元素($item[0])将用于与 $sortOrder 进行匹配。目标是让 $sortOrder 中定义的元素按其在 $sortOrder 中的顺序排列,而未在 $sortOrder 中定义的元素则被放置到数组的末尾。
一个常见的错误实现方式可能如下:
立即学习“PHP免费学习笔记(深入)”;
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;});登录后复制上述代码存在逻辑缺陷:
爱派AiPy 融合LLM与Python生态的开源AI智能体
1 查看详情
if ($valA === false) return -1;:如果 $a[0] 未在 $sortOrder 中找到,此语句会返回 -1,这意味着 $a 会被排在 $b 之前。这与“将未匹配项置于末尾”的目标相悖,因为一个未匹配项不应该排在一个已匹配项之前。if ($valB === false) return 0;:此语句仅在 $valA !== false(即 $a 已找到)的情况下执行。如果此时 $valB === false(即 $b 未找到),返回 0 意味着 $a 和 $b 的相对顺序不变。然而,正确的逻辑应该是将已找到的 $a 排在未找到的 $b 之前,即返回 -1。这些逻辑错误导致当参考数组中只指定了少量值时,排序结果无法达到预期。
解决方案:为未匹配项分配最大权重
要解决这个问题,我们可以为那些未在 $sortOrder 中找到的元素分配一个“虚拟”的、极大的排序权重。这样,在比较时,这些具有极大权重的元素自然会排在所有具有正常(较小)权重的元素之后。PHP 提供了一个常量 PHP_INT_MAX,它代表了 PHP 能处理的最大整数值,非常适合作为这种“极大权重”的代表。
示例代码
以下是使用 PHP_INT_MAX 策略的改进 usort 回调函数:
<?php// 待排序的多维数组$itemsToSort = [ ['itemC', 'dataC'], ['itemA', 'dataA'], ['itemX', 'dataX'], // 未在 $sortOrder 中 ['itemB', 'dataB'], ['itemY', 'dataY'], // 未在 $sortOrder 中 ['itemD', 'dataD'], ['itemA', 'dataA_duplicate'], // 重复项];// 定义排序顺序的参考数组$sortOrder = ['itemA', 'itemB', 'itemC'];echo "原始数组:\n";print_r($itemsToSort);usort($itemsToSort, function($a, $b) use ($sortOrder){ // 获取 $a[0] 在 $sortOrder 中的索引,如果未找到则为 false $rankA = array_search($a[0], $sortOrder); // 获取 $b[0] 在 $sortOrder 中的索引,如果未找到则为 false $rankB = array_search($b[0], $sortOrder); // 如果未找到,将其排序权重设置为 PHP_INT_MAX,确保其排在所有已找到项之后 $rankA = ($rankA === false) ? PHP_INT_MAX : $rankA; $rankB = ($rankB === false) ? PHP_INT_MAX : $rankB; // 比较两个元素的最终排序权重 if ($rankA > $rankB) { return 1; // A 排在 B 之后 } if ($rankA < $rankB) { return -1; // A 排在 B 之前 } return 0; // 权重相等,保持原有相对顺序(或认为相等)});echo "\n排序后的数组:\n";print_r($itemsToSort);?>登录后复制代码解析
array_search($a[0], $sortOrder): 此函数用于查找 $a[0] 在 $sortOrder 数组中的键名(即索引)。如果找到,它会返回相应的整数索引;如果未找到,则返回布尔值 false。($rankA === false) ? PHP_INT_MAX : $rankA;: 这是一个三元运算符。它的作用是:如果 $rankA(或 $rankB)为 false,表示对应的元素未在 $sortOrder 中找到,那么将其排序权重设置为 PHP_INT_MAX。如果 $rankA 不为 false,表示元素已找到,则保留其在 $sortOrder 中的实际索引作为排序权重。最终比较: 经过上述处理后,所有的元素都有了一个数值型的排序权重。我们只需直接比较 $rankA 和 $rankB 即可。if ($rankA > $rankB) return 1;:如果 A 的权重更大(索引值更大或为 PHP_INT_MAX),则 A 排在 B 之后。if ($rankA < $rankB) return -1;:如果 A 的权重更小,则 A 排在 B 之前。return 0;:如果权重相等,则保持它们的相对顺序。这适用于两种情况:一是两个元素都在 $sortOrder 中且具有相同的索引(通常不会发生,除非 $sortOrder 有重复值且 array_search 找到的是第一个);二是两个元素都未在 $sortOrder 中找到,都分配了 PHP_INT_MAX。运行结果
原始数组: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 的顺序排列,
以上就是PHP usort 自定义排序:将未匹配项高效置于数组末尾的策略的详细内容,更多请关注php中文网其它相关文章!



