一、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()配合太空船运算符能写出简洁且可维护的代码。