← PHP array_sum数组求和函数使用指南:从语法到项目实战 PHP array_udiff_assoc函数教程:用自定义回调同时比较键名与值求差集 →

PHP uasort函数教程:用自定义比较函数按值排序数组并保留键名

著
原创 2026-10-09 PHP 已有人查阅

一、uasort解决的是什么问题

PHP的uasort()用来对数组按“值”排序,同时保留键名与值之间的关联关系。和它容易混淆的是usort():usort()也是按值排序,但它会丢弃原有的键名,重新分配从0开始的数字索引。如果你的数组是关联数组,比如["apple"=>12,"banana"=>8],用usort()排完之后键名就没了,变成[0=>8,1=>12],业务上依赖键名取值的地方就会出问题。uasort()正是为这种场景准备的。

语法:

uasort(array &$array, callable $value_compare_func): bool

两个参数:

参数 说明 是否必填
array 要排序的数组,以引用方式传入 必填
value_compare_func 用户自定义的比较函数 必填

返回值:排序成功返回true,失败返回false。

这里有一个细节需要说明:原始资料把第二个参数标为“可选”,这是不准确的。uasort()的第二个参数是必填的,没有比较函数就无法确定排序规则。写代码时如果漏传,PHP会直接报参数错误。

二、比较函数的返回值规则

自定义比较函数接收两个参数$a和$b,代表数组中参与比较的两个值。返回值有三种情况:

  • $a==$b,返回0;

  • $a>$b,返回1;

  • $a<$b,返回-1。

这套规则和usort()、uksort()一致。返回值的正负决定谁排在前面,返回0表示两者顺序不变。需要留意的是,PHP7之后比较函数的返回值会被转换为整数使用,返回浮点数会被截断,所以不要返回0.5这类值来试图做“微调”。

三、代码号学习编程实例

示例1:按运动项目名称升序排列

<?php
function compare($a, $b)
{
    if ($a == $b) return 0;
    return ($a < $b) ? -1 : 1;
}

$sports = [
    "a" => "tennis",
    "b" => "badminton",
    "c" => "football",
    "d" => "cricket"
];

uasort($sports, "compare");
print_r($sports);

输出:

Array
(
    [b] => badminton
    [d] => cricket
    [c] => football
    [a] => tennis
)

可以看到,排序后键名b、d、c、a依然跟着各自的值,没有被替换成数字索引。这就是uasort()和usort()的核心区别。

示例2:按值降序排列

<?php
function usersort($x, $y)
{
    if ($x == $y) return 0;
    return ($x > $y) ? -1 : 1;
}

$car = [
    "1" => "datsun",
    "2" => "tata",
    "5" => "audi",
    "4" => "ford"
];

uasort($car, "usersort");
print_r($car);

输出:

Array
(
    [2] => tata
    [4] => ford
    [1] => datsun
    [5] => audi
)

把比较函数里的-1和1对调,排序方向就反过来了。这种“交换返回值”的写法在项目里很常见,但更推荐用strcmp()或strcasecmp()配合取反,代码更直观。

示例3:字符串值的降序排列

<?php
function myfunction($a, $b)
{
    if ($a == $b) return 0;
    return ($a > $b) ? -1 : 1;
}

$input = [
    "d" => "coconut",
    "a" => "apple",
    "b" => "banana"
];

uasort($input, "myfunction");
print_r($input);

输出:

Array
(
    [d] => coconut
    [b] => banana
    [a] => apple
)

示例4:数字字符串的降序排列

<?php
function sorting($x, $y)
{
    if ($x == $y) return 0;
    return ($x > $y) ? -1 : 1;
}

$input = [
    "d" => "1",
    "a" => "2",
    "b" => "3",
    "f" => "4"
];

uasort($input, "sorting");
print_r($input);

输出:

Array
(
    [f] => 4
    [b] => 3
    [a] => 2
    [d] => 1
)

这里的值虽然是字符串形式的数字,但PHP的比较运算符会按数值规则处理,所以排序结果符合预期。如果值是纯字母字符串,则会按字典序比较。

四、和usort、asort、uksort的区别

这几个函数名字接近,用途不同,放在一起对比更容易记住:

函数 排序依据 是否保留键名 比较方式
usort() 值 不保留 用户自定义
uasort() 值 保留 用户自定义
uksort() 键名 保留 用户自定义
asort() 值 保留 内置规则
ksort() 键名 保留 内置规则

选择逻辑可以这样记:需要保留键名,用带u之外的a或k版本;需要自定义规则,用带u的版本;两者都要满足,就是uasort()或uksort()。很多人在项目里直接上usort(),结果把关联数组的键名冲掉了,回头排查半天,这是比较典型的踩坑点。

五、项目实战经验

1.排行榜按分数排序但保留用户ID

做积分排行榜时,数据结构通常是["user_101"=>880,"user_205"=>960]。如果只关心排序结果、之后还要用用户ID去查数据库,就必须保留键名。这时uasort()是直接可用的选择:

<?php
$scores = [
    "user_101" => 880,
    "user_205" => 960,
    "user_318" => 745
];

uasort($scores, function ($a, $b) {
    return $b <=> $a;
});

print_r($scores);

输出:

Array
(
    [user_205] => 960
    [user_101] => 880
    [user_318] => 745
)

这里用了PHP7引入的太空船运算符<=>,它一次性返回-1、0、1,比手写if判断简洁很多。如果项目还在PHP5,则要退回三元表达式。

2.多字段排序

实际业务里排序条件往往不止一个。比如先按销量降序,销量相同再按价格升序:

<?php
$products = [
    ["name" => "A", "sales" => 100, "price" => 30],
    ["name" => "B", "sales" => 100, "price" => 20],
    ["name" => "C", "sales" => 80,  "price" => 50]
];

uasort($products, function ($a, $b) {
    if ($a["sales"] != $b["sales"]) {
        return $b["sales"] <=> $a["sales"];
    }
    return $a["price"] <=> $b["price"];
});

print_r($products);

多字段排序的关键是:先判断主条件是否相等,不相等就直接返回结果;相等时再进入次条件。这个写法比拼接字符串再比较更可控,也不容易出错。

3.浮点数比较的坑

比较函数里如果直接写$a==$b去判断两个浮点数是否相等,可能因为精度问题返回“不相等”。比如0.1+0.2和0.3在浮点表示下并不相等。涉及金额或科学计算时,我的建议是:

  • 金额统一转成整数分再比较;

  • 或者用abs($a-$b)<0.00001判断近似相等;

  • 不要依赖==做浮点相等判断。

4.排序稳定性

PHP8.0之前,uasort()的排序是不稳定的:值相等的元素,顺序可能被打乱。PHP8.0起,包括uasort()在内的排序函数都变成了稳定排序,相等元素的相对顺序会被保留。如果项目需要兼容PHP7,又依赖相等元素的原始顺序,就要在比较函数里加入额外的“决胜字段”,比如用键名做次级比较:

<?php
uasort($data, function ($a, $b) use ($data) {
    $cmp = $a <=> $b;
    if ($cmp !== 0) return $cmp;
    return array_search($a, $data, true) <=> array_search($b, $data, true);
});

这种做法有性能开销,只在确实需要时才用。

5.回调函数里不要修改数组

uasort()的第一个参数是引用传递。在比较函数内部去读写原数组,容易产生难以预期的行为,甚至影响排序过程。比较函数只负责“比较两个值”,其余逻辑应该放在排序之前或之后处理。

六、本节课程知识要点

  • uasort()按值排序,同时保留键名与值的关联。

  • 第二个参数比较函数是必填的,不是可选。

  • 比较函数返回-1、0、1,分别代表小于、等于、大于。

  • 需要保留键名时用uasort(),不需要保留时用usort()。

  • 按值排序且用内置规则,用asort();按键名排序,用uksort()或ksort()。

  • PHP7及以上推荐使用太空船运算符<=>简化比较逻辑。

  • 多字段排序先判断主条件,再判断次条件。

  • 浮点数不要用==直接比较。

  • PHP8.0起排序是稳定的,PHP7需要自行处理相等元素的顺序。

七、常见问题

问:uasort排序后键名会变吗?

不会。键名保持不变,只是元素在数组中的顺序变了。

问:uasort和asort有什么区别?

asort()用PHP内置的默认比较规则,uasort()用用户自定义的比较函数。需要自定义规则时用uasort()。

问:比较函数返回布尔值可以吗?

不建议。布尔值会被转换为0或1,无法表达“小于”的情况,排序结果可能不符合预期。

问:uasort能对多维数组排序吗?

可以。比较函数里取出需要比较的字段即可,比如$a["price"]<=>$b["price"]。

问:uasort失败会返回什么?

返回false。实际使用中参数类型正确时很少失败,但仍可以通过返回值做基本判断。

uasort()的定位很清楚:按值排序,且保留键名。它的难点不在语法,而在比较函数的写法和边界处理。把usort()、asort()、uksort()几个函数放在一起对比记忆,选择时先问自己两个问题——要不要保留键名,要不要自定义规则,答案就出来了。项目里涉及关联数组排序、排行榜、多字段排序的场景,uasort()配合太空船运算符能写出简洁且可维护的代码。

← PHP array_sum数组求和函数使用指南:从语法到项目实战 PHP array_udiff_assoc函数教程:用自定义回调同时比较键名与值求差集 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号