排序函数
概述
PHP 提供了丰富的排序函数,可以对数组按值、按键、按自定义规则进行排序。排序函数分为两大类:会重建索引的和保持键值关联的。本章详细讲解各种排序函数及其标志选项。
基础概念
排序函数分类
| 函数 | 排序依据 | 保持键 | 重建索引 |
|---|---|---|---|
sort | 值升序 | 否 | 是 |
rsort | 值降序 | 否 | 是 |
asort | 值升序 | 是 | 否 |
arsort | 值降序 | 是 | 否 |
ksort | 键升序 | 是 | 否 |
krsort | 键降序 | 是 | 否 |
usort | 自定义值升序 | 否 | 是 |
uasort | 自定义值升序 | 是 | 否 |
uksort | 自定义键升序 | 是 | 否 |
natsort | 自然排序 | 是 | 否 |
natcasesort | 自然排序(不区分大小写) | 是 | 否 |
array_multisort | 多维/多列排序 | 否 | 是 |
语法与代码
sort / rsort — 值排序(重建索引)
php
<?php
declare(strict_types=1);
// sort - 升序排列,重建数字索引
$fruits = ['banana', 'apple', 'cherry', 'date'];
sort($fruits);
// ['apple', 'banana', 'cherry', 'date']
// rsort - 降序排列,重建数字索引
$numbers = [3, 1, 4, 1, 5, 9, 2, 6];
rsort($numbers);
// [9, 6, 5, 4, 3, 2, 1, 1]
// 注意:sort/rsort 会丢失键名
$assoc = ['name' => 'Alice', 'age' => 30];
sort($assoc);
// [30, 'Alice'](键变成了 0, 1)asort / arsort — 值排序(保持键)
php
<?php
declare(strict_types=1);
// asort - 按值升序,保持键值关联
$prices = ['apple' => 3, 'banana' => 2, 'cherry' => 5];
asort($prices);
// ['banana' => 2, 'apple' => 3, 'cherry' => 5]
// arsort - 按值降序,保持键值关联
arsort($prices);
// ['cherry' => 5, 'apple' => 3, 'banana' => 2]ksort / krsort — 键排序
php
<?php
declare(strict_types=1);
// ksort - 按键升序
$data = ['c' => 3, 'a' => 1, 'b' => 2];
ksort($data);
// ['a' => 1, 'b' => 2, 'c' => 3]
// krsort - 按键降序
krsort($data);
// ['c' => 3, 'b' => 2, 'a' => 1]排序标志
php
<?php
declare(strict_types=1);
$fruits = ['Banana', 'apple', 'Cherry', 'avocado'];
// SORT_REGULAR - 默认(区分大小写)
sort($fruits, SORT_REGULAR);
// ['Banana', 'Cherry', 'apple', 'avocado'](大写字母排前面)
// SORT_STRING - 字符串排序
sort($fruits, SORT_STRING);
// 同上
// SORT_STRING | SORT_FLAG_CASE - 不区分大小写
sort($fruits, SORT_STRING | SORT_FLAG_CASE);
// ['apple', 'avocado', 'Banana', 'Cherry']
// SORT_NUMERIC - 数字排序
$mixed = ['10', '2', '1', '20'];
sort($mixed, SORT_NUMERIC);
// ['1', '2', '10', '20']
// SORT_NATURAL - 自然排序
$files = ['img1.png', 'img10.png', 'img2.png', 'img20.png'];
sort($files, SORT_NATURAL);
// ['img1.png', 'img2.png', 'img10.png', 'img20.png']
// SORT_NATURAL | SORT_FLAG_CASE
natsort($files);
sort($files, SORT_NATURAL | SORT_FLAG_CASE);usort / uasort / uksort — 自定义排序
php
<?php
declare(strict_types=1);
// usort - 自定义值排序(重建索引)
$employees = [
['name' => 'Alice', 'age' => 30],
['name' => 'Bob', 'age' => 25],
['name' => 'Charlie', 'age' => 35],
];
// 按年龄升序
usort($employees, fn(array $a, array $b): int => $a['age'] <=> $b['age']);
// 按姓名降序
usort($employees, fn(array $a, array $b): int => $b['name'] <=> $a['name']);
// uasort - 自定义值排序(保持键)
$scores = ['Alice' => 85, 'Bob' => 92, 'Charlie' => 78];
uasort($scores, fn(int $a, int $b): int => $b <=> $a);
// ['Bob' => 92, 'Alice' => 85, 'Charlie' => 78]
// uksort - 自定义键排序
$data = ['item3' => 3, 'item1' => 1, 'item2' => 2, 'item10' => 10];
uksort($data, fn(string $a, string $b): int => strnatcmp($a, $b));
// ['item1' => 1, 'item2' => 2, 'item3' => 3, 'item10' => 10]natsort / natcasesort — 自然排序
php
<?php
declare(strict_types=1);
// natsort - 自然顺序排序(数字部分按数值排)
$files = ['file1.txt', 'file10.txt', 'file2.txt', 'file20.txt'];
natsort($files);
// ['file1.txt', 'file2.txt', 'file10.txt', 'file20.txt']
// natcasesort - 不区分大小写的自然排序
$images = ['IMG1.jpg', 'img10.jpg', 'img2.jpg', 'Img20.jpg'];
natcasesort($images);
// ['IMG1.jpg', 'img2.jpg', 'img10.jpg', 'Img20.jpg']array_multisort — 多列排序
php
<?php
declare(strict_types=1);
// array_multisort - 多维数组排序
$employees = [
['name' => 'Alice', 'department' => 'eng', 'salary' => 5000],
['name' => 'Bob', 'department' => 'eng', 'salary' => 3000],
['name' => 'Charlie', 'department' => 'mkt', 'salary' => 4000],
['name' => 'Diana', 'department' => 'mkt', 'salary' => 6000],
];
// 提取排序列
$departments = array_column($employees, 'department');
$salaries = array_column($employees, 'salary');
// 先按部门升序,同部门按薪资降序
array_multisort($departments, SORT_ASC, $salaries, SORT_DESC, $employees);
print_r($employees);
// eng: Bob(3000), Alice(5000)
// mkt: Charlie(4000), Diana(6000)详细说明
飞船运算符 <=>
PHP 7.0+ 引入的飞船运算符是编写排序回调的最佳工具:
php
<?php
declare(strict_types=1);
// $a <=> $b
// $a < $b 返回 -1
// $a == $b 返回 0
// $a > $b 返回 1
// 升序: $a <=> $b
// 降序: $b <=> $a选择建议
关联数组使用 asort/arsort/ksort(保持键),索引数组使用 sort/rsort/usort。自定义排序优先使用 usort + 飞船运算符。
实战示例
商品多条件排序
php
<?php
declare(strict_types=1);
$products = [
['name' => 'A', 'price' => 100, 'sales' => 500],
['name' => 'B', 'price' => 50, 'sales' => 1000],
['name' => 'C', 'price' => 100, 'sales' => 300],
['name' => 'D', 'price' => 50, 'sales' => 800],
];
// 先按价格升序,同价按销量降序
usort($products, function (array $a, array $b): int {
$cmp = $a['price'] <=> $b['price'];
return $cmp !== 0 ? $cmp : ($b['sales'] <=> $a['sales']);
});
// 结果: B(50,1000), D(50,800), A(100,500), C(100,300)多维数组排序模式
php
<?php
declare(strict_types=1);
// 多维数组排序的常用模式
// 按 sub-array 的某个字段排序
$employees = [
['name' => 'Alice', 'dept' => 'eng', 'salary' => 5000],
['name' => 'Bob', 'dept' => 'eng', 'salary' => 7000],
['name' => 'Charlie', 'dept' => 'mkt', 'salary' => 4000],
['name' => 'Diana', 'dept' => 'mkt', 'salary' => 6000],
];
// 按 salary 降序
usort($employees, fn(array $a, array $b): int => $b['salary'] <=> $a['salary']);
// 按 dept 升序,同 dept 按 salary 降序
usort($employees, function (array $a, array $b): int {
$cmp = $a['dept'] <=> $b['dept'];
if ($cmp !== 0) return $cmp;
return $b['salary'] <=> $a['salary'];
});
// 封装为通用排序函数
function sortByField(array &$array, string $field, bool $desc = false): void
{
usort($array, function (array $a, array $b) use ($field, $desc): int {
$cmp = $a[$field] <=> $b[$field];
return $desc ? -$cmp : $cmp;
});
}
function sortByFields(array &$array, array $fields): void
{
usort($array, function (array $a, array $b) use ($fields): int {
foreach ($fields as $field => $direction) {
$cmp = $a[$field] <=> $b[$field];
if ($cmp !== 0) {
return $direction === 'desc' ? -$cmp : $cmp;
}
}
return 0;
});
}
sortByFields($employees, ['dept' => 'asc', 'salary' => 'desc']);对象数组排序
php
<?php
declare(strict_types=1);
class Student
{
public function __construct(
public string $name,
public int $score,
public string $grade
) {}
}
$students = [
new Student('Alice', 95, 'A'),
new Student('Bob', 82, 'B'),
new Student('Charlie', 78, 'C'),
new Student('Diana', 92, 'A'),
];
// 按成绩降序
usort($students, fn(Student $a, Student $b): int => $b->score <=> $a->score);
// 按成绩降序,同分按姓名升序
usort($students, function (Student $a, Student $b): int {
$cmp = $b->score <=> $a->score;
return $cmp !== 0 ? $cmp : $a->name <=> $b->name;
});
foreach ($students as $s) {
echo "{$s->name}: {$s->score}\n";
}
// Diana: 92
// Alice: 95
// Bob: 82
// Charlie: 78排序稳定性
php
<?php
declare(strict_types=1);
// PHP 排序不是稳定排序(等值元素的原始顺序可能改变)
// 如需稳定排序,需要在比较函数中加入原始索引
function stableSort(array $array, callable $comparator): array
{
$indexed = [];
foreach ($array as $index => $value) {
$indexed[] = ['index' => $index, 'value' => $value];
}
usort($indexed, function (array $a, array $b) use ($comparator): int {
$cmp = $comparator($a['value'], $b['value']);
return $cmp !== 0 ? $cmp : ($a['index'] <=> $b['index']);
});
return array_column($indexed, 'value');
}
// 使用示例
$data = [
['name' => 'A', 'score' => 90],
['name' => 'B', 'score' => 85],
['name' => 'C', 'score' => 90],
['name' => 'D', 'score' => 85],
];
$sorted = stableSort($data, fn(array $a, array $b): int => $a['score'] <=> $b['score']);
// A 在 C 前面,B 在 D 前面(原始顺序保持)PHP 排序实现
PHP 使用快速排序(quick sort)变体实现排序函数。PHP 8.0+ 优化了排序算法的性能。对于大数据集,排序时间复杂度为 O(n log n)。
注意事项
sort 会重建索引
sort 和 rsort 会丢弃原来的键名,重建为数字索引。关联数组排序请使用 asort/arsort。
排序标志
SORT_FLAG_CASE 必须与 SORT_STRING 或 SORT_NATURAL 一起使用,不能单独使用。
最佳实践
- 关联数组用 asort/arsort:保持键值关联
- 自定义排序用 usort + <=>:简洁高效
- 文件名排序用 natsort:正确的数字排序
- 多列排序用 array_multisort:配合 array_column
- 大小写不敏感用 SORT_FLAG_CASE