數組排序是 PHP 開發高頻操作。內置提供了 sort()、usort()、array_multisort() 等十餘個函數,針對一維/二維、鍵名/鍵值、升序/降序、保持索引/重置索引等不同需求。本文系統梳理所有排序函數、自定義比較函數寫法、二維數組多鍵排序實戰、以及冒泡排序教學實現。
| 函數 | 排序依據 | 順序 | 鍵名保持 | 適用維度 | 返回值 |
|---|---|---|---|---|---|
| sort() | 值 | 升序 | ❌ 重置索引 | 一維 | bool |
| rsort() | 值 | 降序 | ❌ | 一維 | bool |
| asort() | 值 | 升序 | ✅ 保持關聯 | 一維 | bool |
| arsort() | 值 | 降序 | ✅ | 一維 | bool |
| ksort() | 鍵名 | 升序 | ✅ | 一維 | bool |
| krsort() | 鍵名 | 降序 | ✅ | 一維 | bool |
| usort() | 值 (自定義比較) | 自定義 | ❌ | 一維 | bool |
| uasort() | 值 (自定義) | 自定義 | ✅ | 一維 | bool |
| uksort() | 鍵名 (自定義) | 自定義 | ✅ | 一維 | bool |
| array_multisort() | 多數組/多維鍵值 | 混合 | 視情況 | 多維/多數組 | bool |
| natsort() | 值 (自然排序) | 升序 | ✅ | 一維 | bool |
| natcasesort() | 值 (自然排序、大小寫不敏感) | 升序 | ✅ | 一維 | bool |
<?php
$arr = ['d'=>4, 'a'=>1, 'c'=>3, 'b'=>2];
sort($arr); // [0=>1, 1=>2, 2=>3, 3=>4] 重置索引
asort($arr); // ['a'=>1, 'b'=>2, 'c'=>3, 'd'=>4] 保持鍵值關聯
ksort($arr); // ['a'=>1, 'b'=>2, 'c'=>3, 'd'=>4] 按鍵名排序
// 自然排序 (人類閱讀順序:img2.jpg < img10.jpg)
$files = ['img12.jpg', 'img1.jpg', 'img2.jpg'];
sort($files); // ['img1.jpg', 'img12.jpg', 'img2.jpg'] ❌
natsort($files); // ['img1.jpg', 'img2.jpg', 'img12.jpg'] ✅
?>
場景:按「銷量降序、價格升序」排序商品列表。
<?php
$data = [
['id'=>1, 'title'=>'A', 'volume'=>67, 'price'=>20],
['id'=>2, 'title'=>'B', 'volume'=>86, 'price'=>15],
['id'=>3, 'title'=>'C', 'volume'=>85, 'price'=>30],
['id'=>4, 'title'=>'D', 'volume'=>98, 'price'=>25],
['id'=>5, 'title'=>'E', 'volume'=>86, 'price'=>10],
['id'=>6, 'title'=>'F', 'volume'=>67, 'price'=>35],
];
// 步驟 1:提取排序列 (列式數組)
$volumes = array_column($data, 'volume');
$prices = array_column($data, 'price');
// 步驟 2:array_multisort 多鍵排序
// SORT_DESC 降序,SORT_ASC 升序,最後傳入原數組按引用排序
array_multisort($volumes, SORT_DESC, $prices, SORT_ASC, $data);
// 結果:volume 98→86→86→85→67→67;同 volume 86 時 price 10→15
print_r($data);
/*
Array(
[0] => ['volume'=>98, 'price'=>25]
[1] => ['volume'=>86, 'price'=>10] // E
[2] => ['volume'=>86, 'price'=>15] // B
[3] => ['volume'=>85, 'price'=>30]
[4] => ['volume'=>67, 'price'=>20] // A
[5] => ['volume'=>67, 'price'=>35] // F
)
*/
?>
<?php
/**
* 二維數組多鍵排序
* @param array &$data 待排序數組 (引用)
* @param array $sortSpec 排序規范 [
* ['key'=>'volume', 'order'=>SORT_DESC],
* ['key'=>'price', 'order'=>SORT_ASC],
* ]
* @return void
*/
function arrayMultiSort(array &$data, array $sortSpec): void {
if (empty($data) || empty($sortSpec)) return;
$args = [];
foreach ($sortSpec as $spec) {
$col = array_column($data, $spec['key']);
$args[] = $col;
$args[] = $spec['order'] ?? SORT_ASC;
}
$args[] = &$data; // 最後必須是原數組引用
array_multisort(...$args);
}
// 使用
arrayMultiSort($products, [
['key' => 'sales', 'order' => SORT_DESC],
['key' => 'rating', 'order' => SORT_DESC],
['key' => 'price', 'order' => SORT_ASC],
]);
?>
<?php
// 複雜邏輯:優先級 > 時間 > ID
$tasks = [
['id'=>1, 'priority'=>2, 'created'=>'2024-01-15'],
['id'=>2, 'priority'=>1, 'created'=>'2024-01-10'],
['id'=>3, 'priority'=>2, 'created'=>'2024-01-12'],
];
usort($tasks, function($a, $b) {
// 1. 優先級降序
if ($a['priority'] !== $b['priority']) return $b['priority'] <=> $a['priority'];
// 2. 創建時間升序 (舊在前)
$ta = strtotime($a['created']);
$tb = strtotime($b['created']);
if ($ta !== $tb) return $ta <=> $tb;
// 3. ID 升序
return $a['id'] <=> $b['id'];
});
/*
結果:
[
['id'=>3, 'priority'=>2, 'created'=>'2024-01-12'], // 優先級高、時間早
['id'=>1, 'priority'=>2, 'created'=>'2024-01-15'],
['id'=>2, 'priority'=>1, 'created'=>'2024-01-10'],
]
*/
?>
<?php
// 返回 -1 (小於)、0 (相等)、1 (大於)
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
echo 2 <=> 2; // 0
// 鏈式比較:完美替代嵌套 if-else
usort($arr, fn($a, $b) =>
($b['score'] <=> $a['score']) // 分數降序
?: ($a['time'] <=> $b['time']) // 時間升序
?: ($a['id'] <=> $b['id']) // ID 升序
);
?>
<?php
/**
* 冒泡排序 (一維/二維數組指定鍵)
* 兩兩比較相鄰元素,逆序則交換,大元素「冒泡」到末尾
* @param array $array 待排序數組 (引用修改)
* @param string|null $key 二維數組排序鍵
* @return array 排序後數組
*/
function bubbleSort(array &$array, ?string $key = null): array {
$n = count($array);
for ($i = 0; $i < $n - 1; $i++) {
for ($j = 0; $j < $n - 1 - $i; $j++) {
$valJ = $key ? $array[$j][$key] : $array[$j];
$valJ1 = $key ? $array[$j+1][$key] : $array[$j+1];
if ($valJ > $valJ1) {
[$array[$j], $array[$j+1]] = [$array[$j+1], $array[$j]]; // 交換
}
}
}
return $array;
}
// 使用
$nums = [64, 34, 25, 12, 22, 11, 90];
bubbleSort($nums);
print_r($nums); // [11, 12, 22, 25, 34, 64, 90]
$products = [
['name'=>'A', 'price'=>50],
['name'=>'B', 'price'=>20],
['name'=>'C', 'price'=>80],
];
bubbleSort($products, 'price');
print_r($products); // 按 price 升序
?>
| 場景 | 推薦函數 | 關鍵點 |
|---|---|---|
| 一維索引數組值排序、不保留鍵 | sort()/rsort() | 最快、重置索引 |
| 一維關聯數組值排序、保留鍵值關聯 | asort()/arsort() | 保持鍵值對應 |
| 一維數組按鍵名排序 | ksort()/krsort() | 鍵名字典序 |
| 一維數組複雜邏輯排序 | usort()/uasort() | 自定義比較函數、配合 <=> |
| 二維/多維數組多鍵排序 | array_multisort() | 提取列 → 多鍵排序 → 原數組引用 |
| 自然語言排序 (文件名、版本號) | natsort()/natcasesort() | img2 < img10 |
| 教學/面試/極小數組 | 冒泡/快排手寫 | 理解原理、非生產用 |
array_multisort() 需提取列佔用額外內存,超大數組 (百萬行) 考慮分塊或數據庫排序。array_multisort() 替代。結語:PHP 排序函數族豐富且強大,掌握「值/鍵、升/降、保持/重置、一維/多維、內置/自定義」五個維度的組合,配合 <=> 運算符與 array_column() 提取列,能優雅解決 99% 的業務排序需求。