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

PHP 陣列去重完整指南:array_unique 進階用法、多維陣列去重、保留鍵名與效能對比

     編輯:PHP 相關知識

array_unique() 是 PHP 內建去重函數,但預設行為「重新索引數字鍵、保留字串鍵、比較值轉字串」常導致意外結果。本文系統講解一維/多維陣列去重的各種場景、陷阱與最佳實踐代碼。

基礎行為回顧

<?php
$arr = [100, 200, 100, '100', 300];
print_r(array_unique($arr));
// 輸出:[0=>100, 1=>200, 4=>300]
// 注意:整數 100 與字串 '100' 視為相同 (鬆散比較)
// 數字鍵 0,2 重複值被移除,保留第一個 (鍵 0),鍵 4 保留
?>

核心參數:$sort_flags (PHP 5.2.10+)

場景一:一維陣列 - 保留原鍵名

$arr = ['a' => 1, 'b' => 2, 'c' => 1, 'd' => 3];
$unique = array_unique($arr, SORT_REGULAR);
// 結果:['a'=>1, 'b'=>2, 'd'=>3] (字串鍵保留)

$arr = [10, 20, 10, 30];
$unique = array_unique($arr, SORT_REGULAR);
// 結果:[0=>10, 1=>20, 3=>30] (數字鍵保留第一個出現的鍵)

場景二:多維陣列去重 (核心痛點)

array_unique 無法直接處理多維陣列 (會觸發 Array to string conversion Notice)。

方案 A:序列化比較 (通用、保留結構)

function arrayUniqueMulti(array $arr): array {
    $serialized = array_map('serialize', $arr);
    $unique = array_unique($serialized, SORT_REGULAR);
    return array_map('unserialize', $unique);
}

$data = [
    ['id'=>1, 'name'=>'A'],
    ['id'=>2, 'name'=>'B'],
    ['id'=>1, 'name'=>'A'], // 重複
    ['id'=>3, 'name'=>'C'],
];
print_r(arrayUniqueMulti(data));
// 保留鍵 0,1,3

方案 B:JSON 編碼比較 (效能較好、需注意編碼一致性)

function arrayUniqueMultiJson(array $arr): array {
    $encoded = array_map(fn($v) => json_encode($v, JSON_UNESCAPED_UNICODE|JSON_SORT_KEYS), $arr);
    $unique = array_unique($encoded, SORT_REGULAR);
    return array_map('json_decode', $unique, [true]); // true = 關聯陣列
}

方案 C:自定義比較鍵 (大數據高效)

function arrayUniqueByKey(array $arr, string $key): array {
    $seen = [];
    return array_filter($arr, function($item) use ($key, &$seen) {
        $val = $item[$key] ?? null;
        if (isset($seen[$val])) return false;
        $seen[$val] = true;
        return true;
    });
}

$data = [
    ['id'=>1, 'name'=>'A', 'email'=>'a@x.com'],
    ['id'=>2, 'name'=>'B', 'email'=>'b@x.com'],
    ['id'=>1, 'name'=>'A', 'email'=>'a2@x.com'], // id 重複
];
print_r(arrayUniqueByKey($data, 'id'));
// 保留第一筆 id=1

場景三:大陣列效能優化 (10萬+ 元素)

array_unique 內部排序複雜度 O(n log n)。若只需「保留第一次出現」,用 array_flip 雙翻轉 O(n):

function arrayUniqueFast(array $arr): array {
    // 僅適用值為純量 (可作鍵) 的陣列
    return array_flip(array_flip($arr));
}

// 測試
$arr = range(1, 100000);
$arr = array_merge($arr, $arr); // 20萬元素,50% 重複

$t = microtime(true); array_unique($arr); echo "array_unique: " . (microtime(true)-$t) . "s\n";
$t = microtime(true); arrayUniqueFast($arr); echo "flip*2: " . (microtime(true)-$t) . "s\n";
// 典型輸出:array_unique: 0.045s, flip*2: 0.008s (快 5-6 倍)

限制:值必須為 int/float/string/bool/null (可作陣列鍵),且會重新索引數字鍵 (0,1,2...)。

場景四:物件陣列去重

class User { public int $id; public string $name; }
$users = [
    new User(1, 'A'),
    new User(2, 'B'),
    new User(1, 'A'), // 同屬性值
];

// 方案:spl_object_id (PHP 7.2+) 比較實例身份
$unique = [];
$ids = [];
foreach ($users as $u) {
    $oid = spl_object_id($u);
    if (!isset($ids[$oid])) {
        $ids[$oid] = true;
        $unique[] = $u;
    }
}

// 方案:自定義比較邏輯 (如比較 id 屬性)
$unique = [];
$seenIds = [];
foreach ($users as $u) {
    if (!isset($seenIds[$u->id])) {
        $seenIds[$u->id] = true;
        $unique[] = $u;
    }
}

常見陷阱總結

  1. 預設字串比較:100 == '100' 導致類型不同但值相同被視為重複 → 永遠加 SORT_REGULAR。
  2. 多維陣列報 Notice:必須序列化/JSON 編碼後比較。
  3. 鍵名重置:array_unique 保留字串鍵,但數字鍵會保留第一個出現的鍵 (非連續)。若需連續索引 → array_values()。
  4. 大陣列記憶體:序列化/JSON 會產生大量臨時字串,超大陣列考慮分塊處理或資料庫 DISTINCT。
  5. 物件比較:預設比較屬性值 (如同陣列),但 SORT_REGULAR 可能不生效,建議自定義邏輯。

最佳實踐速查表

常數比較模式說明
SORT_STRING字串比較預設。100 === '100' 為真。
SORT_REGULAR標準比較 (不轉型)推薦。100 !== '100',類型也要相同。
SORT_NUMERIC數值比較'100' == 100 為真。
SORT_LOCALE_STRING當地化字串比較依 setlocale()。
場景推薦方案關鍵點
一維純量陣列、保留鍵array_unique($arr, SORT_REGULAR)加 SORT_REGULAR
一維純量、極大陣列、可重置鍵array_flip(array_flip($arr))O(n) 極快
多維陣列、完整結構比較array_map('unserialize', array_unique(array_map('serialize', $arr), SORT_REGULAR))通用、保留鍵
多維陣列、指定欄位去重arrayUniqueByKey($arr, 'id')O(n)、業務語義清晰
物件陣列、比較屬性自定義循環 + 看見集合可控、效能好

去重看似簡單,選對工具與參數能省下大量 CPU 與記憶體。記住「SORT_REGULAR 是預設陷阱的解藥」、「多維必須序列化」、「大陣列用翻轉法」三大原則,應對 99% 場景綽綽有餘。

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