處理陣列時,array_filter 與 array_unique 是最高頻工具。本文詳細說明兩者用法、自定義回呼、效能對比,並提供「二維陣列去重」通用函式(原生 array_unique 僅支援一維)。
$arr = [0, 1, false, 2, '', 3, null, 'hello', []];
$filtered = array_filter($arr); // 無回呼時,移除等同 false 的值
// 結果:[1 => 1, 3 => 2, 5 => 3, 7 => 'hello']
// 注意:鍵名保留原索引,若需重新索引用 array_values()
$arr = [1,2,3,4,5,6,7,8,9,10];
$odd = array_filter($arr, function($v) { return $v & 1; });
// [0=>1, 2=>3, 4=>5, 6=>7, 8=>9]
$arr = ['a'=>1, 'b'=>2, 'c'=>3, 'd'=>4];
// 依鍵名過濾
$byKey = array_filter($arr, function($k) { return $k !== 'b'; }, ARRAY_FILTER_USE_KEY);
// 同時使用鍵值
$byBoth = array_filter($arr, function($v, $k) { return $v > 2 && $k !== 'd'; }, ARRAY_FILTER_USE_BOTH);
$arr = [3, 1, 2, 3, 4, 1, 5];
$unique = array_unique($arr); // [0=>3, 1=>1, 2=>2, 4=>4, 6=>5]
// 同樣保留首次出現的鍵名
$arr = array_merge(range(1,100), range(1,100)); // 200 元素,重複度 50%
// 方法 1:foreach + in_array
$t1 = microtime(true);
$res1 = [];
foreach ($arr as $v) { if (!in_array($v, $res1)) $res1[] = $v; }
$t1 = microtime(true) - $t1;
// 方法 2:array_unique
$t2 = microtime(true);
$res2 = array_unique($arr);
$t2 = microtime(true) - $t2;
echo "foreach: $t1 秒\nunique: $t2 秒\n";
// 結論:資料量 < 1000 差異微小;> 10000 且重複度高時 array_unique 明顯更快
原生 array_unique 遇到陣列元素會拋出 Warning。解法:將子陣列序列化為字串去重,再還原。
/**
* 二維陣列去重(保留鍵名可選)
* @param array $arr2D 二維陣列
* @param bool $keepKeys 是否保留一層鍵名
* @param bool $keepSubKeys 是否保留二層鍵名(需所有子陣列鍵名相同)
* @return array
*/
function array2dUnique(array $arr2D, bool $keepKeys = false, bool $keepSubKeys = true): array {
// 1. 降維:子陣列 -> 字串
$serialized = [];
foreach ($arr2D as $k => $v) {
$serialized[$k] = serialize($v); // 或 json_encode($v, JSON_UNESCAPED_UNICODE|JSON_SORT_KEYS)
}
// 2. 去重
$uniqueSerial = array_unique($serialized);
// 3. 還原
$result = [];
foreach ($uniqueSerial as $k => $s) {
$newKey = $keepKeys ? $k : null;
$val = unserialize($s); // 或 json_decode($s, true)
if ($keepSubKeys) {
$result[$newKey] = $val;
} else {
$result[$newKey] = array_values($val);
}
}
return $result;
}
// 測試
$data = [
'first' => ['title' => 'A', 'date' => '2023-01-01'],
'second' => ['title' => 'A', 'date' => '2023-01-01'], // 重複
'third' => ['title' => 'B', 'date' => '2023-01-02'],
];
print_r(array2dUnique($data, true));
/*
Array
(
[first] => Array ( [title] => A [date] => 2023-01-01 )
[third] => Array ( [title] => B [date] => 2023-01-02 )
)
*/
function uniqueByField(array $arr, string $field): array {
$seen = [];
return array_filter($arr, function($item) use ($field, &$seen) {
$val = $item[$field] ?? null;
if ($val === null || isset($seen[$val])) return false;
$seen[$val] = true;
return true;
});
}
$users = [
['id'=>1, 'name'=>'A'],
['id'=>2, 'name'=>'B'],
['id'=>1, 'name'=>'A dup'], // 重複 id
];
print_r(uniqueByField($users, 'id'));
// 保留首筆 id=1
| 需求 | 函式/方法 | 注意 |
|---|---|---|
| 移除空值/假值 | array_filter($arr) | 保留鍵名,配合 array_values 重排 |
| 自定義邏輯過濾 | array_filter($arr, fn($v)=>...) | 可用 ARRAY_FILTER_USE_KEY/BOTH |
| 一維去重 | array_unique($arr) | 大陣列效能優於 foreach+in_array |
| 二維去重(整行比對) | array2dUnique($arr) | 內部用 serialize/json,支援巢狀結構 |
| 指定欄位去重 | uniqueByField($arr, 'id') | 保留首次出現,適用業務主鍵 |