在處理多組資料時,常需進行合併 (Union)、交集、差集等集合運算。PHP 提供 array_merge()、array_intersect()、array_diff() 等函式,但其行為在**字串鍵名 vs 數值鍵名**、**重複值處理**、**多陣列參數**等細節上有顯著差異。本文深度解析各函式特性與最佳實踐。
<?php
$a = [1 => 'a', 'b', 'c']; // [1=>'a', 0=>'b', 1=>'c'] 實際為 [1=>'a', 0=>'b', 1=>'c'] → 最後 1=>'c'
$b = [1 => 'aa', 2, 'c']; // [1=>'aa', 0=>2, 1=>'c'] → 最後 1=>'c'
// array_merge: 數值鍵重新索引,字串鍵後者勝出
$merged = array_merge($a, $b);
/* 結果:
[0] => 'a' (原 $a[1])
[1] => 'b' (原 $a[0])
[2] => 'c' (原 $a[1] 即 'c')
[3] => 'aa' (原 $b[1])
[4] => 2 (原 $b[0])
[5] => 'c' (原 $b[1] 即 'c')
*/
// + 運算子: 先勝出,保留原鍵名
$union = $a + $b;
/* 結果:
[1] => 'a' ($a[1] 勝出)
[0] => 'b' ($a[0])
[2] => 2 ($b[0] 數值鍵 0 未在 $a 中)
*/
?>
<?php
$a2 = ['str' => 'a', 'b', 'c'];
$b2 = ['str' => 'aa', 2, 'c'];
$merged2 = array_merge($a2, $b2);
// ['str' => 'aa', 0 => 'b', 1 => 'c', 2 => 2, 3 => 'c'] // 'str' 後者勝出
$union2 = $a2 + $b2;
// ['str' => 'a', 0 => 'b', 1 => 'c'] // 'str' 先勝出
?>
<?php
$merged = array_merge($a, $b);
$unique = array_unique($merged); // 移除重複值,保留第一次出現的鍵名
// 注意:array_unique 保留鍵名,可能導致索引不連續
$reindexed = array_values($unique); // 重新索引 0..n
?>
<?php
$a = ['jpg', 'png', 'gif', 'bmp'];
$b = ['JPG', 'txt', 'docx', 'bmp'];
$intersection = array_intersect($a, $b);
// 結果: [3 => 'bmp'] (大小寫敏感,只有 'bmp' 完全相同)
?>
<?php
// 方法 1: 先統一大小寫再交集
$intersection2 = array_intersect(
array_map('strtolower', $a),
array_map('strtolower', $b)
);
// 結果: [0 => 'jpg', 3 => 'bmp']
// 方法 2: 自訂比較函式 (保留原鍵名與原值)
$intersection3 = array_uintersect($a, $b, function($x, $y) {
return strcasecmp($x, $y);
});
// 結果: [0 => 'jpg', 3 => 'bmp'] 保留原始大小寫
?>
<?php
$a = ['a' => 1, 'b' => 2, 'c' => 3];
$b = ['a' => 1, 'b' => 99, 'd' => 4];
array_intersect($a, $b); // 值交集: ['a'=>1, 'c'=>3] (值 1 和 3 都在 $b 中)
array_intersect_assoc($a, $b); // 鍵值雙重交集: ['a'=>1] (只有 'a'=>1 完全相同)
?>
<?php
$old = ['jpg', 'png', 'gif', 'bmp'];
$new = ['JPG', 'txt', 'docx', 'bmp'];
$diff = array_diff($old, $new);
// 結果: [0 => 'jpg', 1 => 'png', 2 => 'gif'] (大小寫敏感,'jpg' != 'JPG')
?>
<?php
$diff2 = array_diff(
array_map('strtolower', $old),
array_map('strtolower', $new)
);
// 結果: [0 => 'jpg', 1 => 'png', 2 => 'gif']
// 保留原值大小寫的差集
$diff3 = array_udiff($old, $new, function($x, $y) {
return strcasecmp($x, $y);
});
// 結果: [0 => 'jpg', 1 => 'png', 2 => 'gif']
?>
<?php
$a = ['a' => 1, 'b' => 2, 'c' => 3];
$b = ['a' => 1, 'b' => 99, 'd' => 4];
array_diff($a, $b); // 值差集: ['b'=>2, 'c'=>3] (值 2,3 不在 $b)
array_diff_assoc($a, $b); // 鍵值雙重差集: ['b'=>2, 'c'=>3] ('b'=>2 不同, 'c' 不在 $b)
?>
所有上述函式均支援 **3 個或更多陣列參數**:
<?php
$a = [1, 2, 3, 4, 5];
$b = [3, 4, 5, 6, 7];
$c = [5, 6, 7, 8, 9];
array_merge($a, $b, $c); // 依序合併
array_intersect($a, $b, $c); // 三者共同擁有的值: [5]
array_diff($a, $b, $c); // 在 $a 但不在 $b 和 $c: [1, 2]
?>
| 操作 | 時間複雜度 | 空間複雜度 | 備註 |
|---|---|---|---|
| array_merge | O(N) | O(N) | 建立新陣列 |
| array_intersect | O(N×M) | O(min(N,M)) | 內部使用雜湊表查找 |
| array_diff | O(N×M) | O(N) | 建立查找表 |
| array_unique | O(N) | O(N) | 排序後去重 |
$lookup = array_flip($largeArray); 然後 isset($lookup[$value]) O(1) 查找array_chunk() 分批UNION/INTERSECT/EXCEPT 或 JOIN 通常更高效<?php
$userRoles = ['user', 'editor'];
$groupRoles = ['editor', 'admin'];
$explicitPermissions = ['publish', 'delete'];
// 合併所有權限 (去重)
$allPermissions = array_unique(array_merge($userRoles, $groupRoles, $explicitPermissions));
// ['user', 'editor', 'admin', 'publish', 'delete']
// 檢查是否擁有特定權限
function hasPermission($permissions, $required) {
return in_array($required, $permissions, true); // 嚴格比較
}
?>
<?php
$oldIds = [1, 2, 3, 4, 5]; // 資料庫現有 ID
$newIds = [3, 4, 5, 6, 7]; // API 回傳 ID
$toDelete = array_diff($oldIds, $newIds); // [1, 2] 需刪除
$toInsert = array_diff($newIds, $oldIds); // [6, 7] 需新增
$toUpdate = array_intersect($oldIds, $newIds); // [3, 4, 5] 需更新
?>
<?php
$required = ['name', 'email', 'phone', 'address'];
$submitted = array_keys($_POST); // 實際提交的欄位
$missing = array_diff($required, $submitted);
if ($missing) {
throw new Exception('缺少必填欄位: ' . implode(', ', $missing));
}
?>
| 需求 | 推薦函式 | 關鍵行為 |
|---|---|---|
| 合併多個陣列,數值鍵重新索引 | array_merge() | 後者覆蓋字串鍵 |
| 合併兩個陣列,保留原鍵名,先勝出 | $a + $b | 不重新索引 |
| 找出多個陣列共同的值 | array_intersect() | 保留第一個陣列鍵名 |
| 找出共同的鍵值對 | array_intersect_assoc() | 鍵+值都相同 |
| 找出在 A 但不在 B 的值 | array_diff() | 保留 A 的鍵名 |
| 找出在 A 但不在 B 的鍵值對 | array_diff_assoc() | 鍵+值都不同 |
| 不區分大小寫比較 | array_uintersect / array_udiff | 自訂比較函式 |
| 移除重複值 | array_unique() | 保留第一次出現的鍵名 |
_assoc 尾綴函式array_map('strtolower') 或 _u 系列自訂比較熟練掌握陣列集合運算,能以宣告式程式碼替代複雜的巢狀迴圈邏輯,提升可讀性與維護性。