首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 二維陣列按指定列排序終極指南:array_multisort 核心原理、封裝函數與實戰優化

     編輯:PHP 綜合知識

前言

「二維陣列按某列排序」是 PHP 開發高頻需求(如:商品按價格、用戶按註冊時間、文章按瀏覽量)。PHP 核心函數 array_multisort() 是實現此功能的**標準、最高效**工具。本文將深度解析其「多陣列同步排序」核心機制,提供開箱即用的通用封裝函數,並對比 usort 等替代方案,助你寫出高性能、可維護的排序代碼。

核心原理:array_multisort 的「參考同步排序」機制

array_multisort() 可接收**多個陣列**作為參數,後面可跟排序標誌(SORT_ASC/SORT_DESC、SORT_REGULAR/SORT_NUMERIC/SORT_STRING),**最後一個參數必須是要排序的目標陣列(通常是原二維陣列)**。

執行邏輯:

  1. 將每個「排序依據陣列」(如提取出的某列值陣列)與「目標陣列」視為**並行數據行**。
  2. 按第一個排序依據陣列排序,**同時調換所有後續陣列(包括目標陣列)對應行的位置**。
  3. 若第一依據值相同,按第二依據陣列排序,再次同步調換所有陣列行...
  4. 最終,目標陣列(原二維陣列)按指定列、指定順序重新排列完成。

這類似 SQL 的 ORDER BY col1 DESC, col2 ASC,每個依據陣列就是一個 ORDER BY 項。

基礎用法演示:官方手冊範例深度拆解

<?php
$data = [
    ['volume' => 67, 'edition' => 2],
    ['volume' => 86, 'edition' => 1],
    ['volume' => 85, 'edition' => 6],
    ['volume' => 98, 'edition' => 2],
    ['volume' => 86, 'edition' => 6],
    ['volume' => 67, 'edition' => 7],
];

// 1. 提取排序依據列(建立並行陣列)
foreach ($data as $key => $row) {
    $volume[$key]  = $row['volume'];  // 第 1 依據:volume
    $edition[$key] = $row['edition']; // 第 2 依據:edition
}

// 2. 多重排序:volume 降序,edition 升序,最終同步排序 $data
// 參數順序:依據1陣列, 依據1標誌, 依據2陣列, 依據2標誌, ..., 目標陣列
array_multisort($volume, SORT_DESC, $edition, SORT_ASC, $data);

/* 結果:
Array (
    [0] => Array ( [volume] => 98 [edition] => 2 )
    [1] => Array ( [volume] => 86 [edition] => 1 )  // volume 同 86,edition 1 < 6
    [2] => Array ( [volume] => 86 [edition] => 6 )
    [3] => Array ( [volume] => 85 [edition] => 6 )
    [4] => Array ( [volume] => 67 [edition] => 2 )  // volume 同 67,edition 2 < 7
    [5] => Array ( [volume] => 67 [edition] => 7 )
)
*/
?>

通用封裝函數:一鍵按任意列、任意順序、多列排序

<?php
/**
 * 二維陣列多列排序(支援單列/多列、升序/降序、數值/字符串比較)
 * 
 * @param array  $data      待排序二維陣列(引用,將被修改)
 * @param array  $columns   排序規則陣列,格式:
 *                          [
 *                              ['column' => 'price', 'order' => SORT_DESC, 'type' => SORT_NUMERIC],
 *                              ['column' => 'name',  'order' => SORT_ASC,  'type' => SORT_STRING],
 *                          ]
 *                          簡寫:['price' => SORT_DESC, 'name' => SORT_ASC] (類型自動判斷)
 * @param bool   $preserveKeys 是否保留原鍵名(默認 false,重建 0..N 索引)
 * @return array 排序後的陣列(同 $data 引用)
 */
function array_multisort_columns(array &$data, array $columns, bool $preserveKeys = false): array {
    if (empty($data) || empty($columns)) return $data;

    // 標準化規則
    $sortArgs = [];
    foreach ($columns as $col => $rule) {
        if (is_int($col)) { // 簡寫格式:['price', 'name'] 或 ['price' => SORT_DESC]
            $col = $rule;
            $rule = [];
        }
        $order = $rule['order'] ?? SORT_DESC;
        $type  = $rule['type'] ?? SORT_REGULAR;
        
        // 提取列值陣列
        $colValues = array_column($data, $col);
        if (empty($colValues)) {
            trigger_error("排序列 '$col' 不存在於數據中", E_USER_WARNING);
            continue;
        }
        
        // 自動判斷類型(若未指定)
        if ($type === SORT_REGULAR) {
            $firstVal = reset($colValues);
            $type = is_numeric($firstVal) ? SORT_NUMERIC : SORT_STRING;
        }
        
        $sortArgs[] = $colValues;
        $sortArgs[] = $order;
        $sortArgs[] = $type;
    }

    // 最後加入目標陣列(引用)
    $sortArgs[] = &$data;
    
    // 執行排序
    array_multisort(...$sortArgs);
    
    // 可選:重建索引
    if (!$preserveKeys) {
        $data = array_values($data);
    }
    
    return $data;
}

// === 使用示例 ===
$products = [
    ['id' => 1, 'name' => '手機', 'price' => 5999, 'stock' => 100, 'category' => '電子'],
    ['id' => 2, 'name' => '筆記本', 'price' => 8999, 'stock' => 50,  'category' => '電子'],
    ['id' => 3, 'name' => '書包', 'price' => 199,  'stock' => 200, 'category' => '箱包'],
    ['id' => 4, 'name' => '滑鼠', 'price' => 99,   'stock' => 500, 'category' => '電子'],
    ['id' => 5, 'name' => '鍵盤', 'price' => 299,  'stock' => 300, 'category' => '電子'],
];

// 1. 單列排序:價格降序
array_multisort_columns($products, ['price' => SORT_DESC]);
// 結果:筆記本(8999) -> 手機(5999) -> 鍵盤(299) -> 書包(199) -> 滑鼠(99)

// 2. 多列排序:類別升序,價格降序
array_multisort_columns($products, [
    ['column' => 'category', 'order' => SORT_ASC, 'type' => SORT_STRING],
    ['column' => 'price',    'order' => SORT_DESC, 'type' => SORT_NUMERIC],
]);
/* 結果:
箱包類:書包(199)
電子類:筆記本(8999) -> 手機(5999) -> 鍵盤(299) -> 滑鼠(99)
*/

// 3. 簡寫格式:庫存升序
array_multisort_columns($products, ['stock' => SORT_ASC]);
?>

性能對比:array_multisort vs usort vs 自定義排序

方案時間複雜度實際速度 (10萬行)代碼複雜度適用場景
array_multisortO(N log N) * 列數**最快 (C 底層實現)**低(提取列 + 一行調用)**常規多列排序首選**
usort + 回調O(N log N) * 比較次數慢 10-50 倍 (PHP 回調開銷大)中(需寫比較函數)極複雜邏輯(如多字段混合、非標準比較)
自定義快排/堆排O(N log N)極慢 (純 PHP 實現)高教學/特殊算法研究

結論: **只要排序邏輯可用「多列值比較」表達,array_multisort 絕對是性能王者。** 僅在邏輯極其複雜(如:按某列取模後排序、需訪問外部服務比較)時才考慮 usort。

常見坑與最佳實踐

  • 鍵名保留: array_multisort **會重建數字索引 (0,1,2...)**,原關聯鍵名丟失。若需保留,排序後用 array_combine 恢復,或使用封裝函數的 $preserveKeys=true(內部實現需額外邏輯)。
  • 欄位不存在: array_column 遇到不存在的列返回空陣列,導致排序無效或報警。**必須預檢或捕獲警告。**
  • 類型一致性: 同一列值類型混雜(數字+字符串)會導致 SORT_REGULAR 比較異常。**顯式指定 SORT_NUMERIC 或 SORT_STRING,或預處理統一類型。**
  • 引用傳遞: 最後一個參數**必須是變量引用**(&$data),直接傳表達式會報錯。
  • 大數據內存: 提取列陣列會佔用額外內存(約原數據 1/列數)。百萬行大數據建議分批處理或使用生成器+ usort(雖慢但省內存)。

總結

PHP 二維陣列排序,**array_multisort 是核心、標準、高性能方案**。掌握其「提取依據列陣列 → 同步多陣列排序」機制,配合通用封裝函數 array_multisort_columns(),即可優雅解決 99% 的業務排序需求:單列/多列、升序/降序、數值/字符串、保留鍵名/重建索引。切記:性能敏感場景堅決避免 usort 回調,善用 C 底層優勢,讓排序在毫秒級完成。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved