0

0

PHP usort 自定义排序:将未匹配项高效置于数组末尾的策略

碧海醫心

碧海醫心

发布时间:2025-11-26 13:34:02

|

587人浏览过

|

来源于php中文网

原创

PHP usort 自定义排序:将未匹配项高效置于数组末尾的策略

本文详细探讨了如何使用 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;
});

上述代码存在逻辑缺陷:

歌者PPT
歌者PPT

歌者PPT,AI 写 PPT 永久免费

下载
  1. if ($valA === false) return -1;:如果 $a[0] 未在 $sortOrder 中找到,此语句会返回 -1,这意味着 $a 会被排在 $b 之前。这与“将未匹配项置于末尾”的目标相悖,因为一个未匹配项不应该排在一个已匹配项之前。
  2. 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);

?>

代码解析

  1. array_search($a[0], $sortOrder): 此函数用于查找 $a[0] 在 $sortOrder 数组中的键名(即索引)。如果找到,它会返回相应的整数索引;如果未找到,则返回布尔值 false。
  2. ($rankA === false) ? PHP_INT_MAX : $rankA;: 这是一个三元运算符。它的作用是:
    • 如果 $rankA(或 $rankB)为 false,表示对应的元素未在 $sortOrder 中找到,那么将其排序权重设置为 PHP_INT_MAX。
    • 如果 $rankA 不为 false,表示元素已找到,则保留其在 $sortOrder 中的实际索引作为排序权重。
  3. 最终比较: 经过上述处理后,所有的元素都有了一个数值型的排序权重。我们只需直接比较 $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 的顺序排列,

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1568

2023.10.24

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1568

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

241

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

150

2025.10.17

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

150

2025.10.17

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

847

2023.08.22

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

761

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

221

2023.09.04

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 13.5万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号