在 PHP 開發中,經常需要將兩個關聯數組根據某個共同字段(如 id)進行合併,類似於 SQL 的 LEFT JOIN 操作。最直觀的寫法是使用雙層 foreach 循環遍歷匹配,但這種做法時間複雜度為 O(n×m),數據量大時性能極差。本文介紹一種利用「數組下標索引」的優化方案,將時間複雜度降至 O(n+m),大幅提升執行效率。
假設有兩個從數據庫查詢得到的數組:
$test1 = [
0 => ['id' => 9478137, 'create_time' => 1394760724],
1 => ['id' => 9478138, 'create_time' => 1394760725],
2 => ['id' => 9478138, 'create_time' => 1394760725]
];
$test2 = [
0 => ['id' => 9478137, 'message' => 'love you'],
1 => ['id' => 9478138, 'message' => 'miss you']
];
需求:以 $test1 為主表,根據 id 字段將 $test2 的 message 合併進去,保留 $test1 的所有記錄(即使重複 id 也要全部保留),類似 LEFT JOIN 效果。
function _mergerArray($array1, $array2, $field1, $field2 = '') {
$ret = [];
foreach ($array1 as $key1 => $value1) {
foreach ($array2 as $key2 => $value2) {
if ($value1[$field1] == $value2[$field2]) {
$ret[$key1] = array_merge($value1, $value2);
}
}
}
return $ret;
}
缺點:每條 $array1 記錄都要遍歷整個 $array2,數據量達萬級時明顯卡頓。
核心思路:先遍歷 $array2,以關聯字段值為鍵建立「查找表」;再遍歷 $array1 直接通過鍵值 O(1) 取得對應數據並合併。
function _mergerArray($array1, $array2, $field1, $field2 = '') {
$ret = [];
$field2 = $field2 ?: $field1; // 若未指定 field2,默認與 field1 相同
// 步驟 1:將 $array2 轉為「鍵值 => 完整記錄」的映射表
$lookup = [];
foreach ($array2 as $item) {
$lookup[$item[$field1]] = $item;
}
// 步驟 2:遍歷 $array1,直接通過鍵值合併
foreach ($array1 as $item) {
$key = $item[$field1];
if (isset($lookup[$key])) {
$ret[] = array_merge($lookup[$key], $item);
} else {
$ret[] = $item; // 無匹配時保留原記錄(LEFT JOIN 語義)
}
}
return $ret;
}
// 測試
$ret = _mergerArray($test1, $test2, 'id', 'id');
print_r($ret);
Array
(
[0] => Array
(
[id] => 9478137
[message] => love you
[create_time] => 1394760724
)
[1] => Array
(
[id] => 9478138
[message] => miss you
[create_time] => 1394760725
)
[2] => Array
(
[id] => 9478138
[message] => miss you
[create_time] => 1394760725
)
)
完全符合 LEFT JOIN 預期:$test1 的三條記錄全部保留,重複 id 也各自合併了對應的 message。
若 $array2 存在重複鍵值,後出現的記錄會覆蓋先前的(類似 SQL 中無唯一約束時的行為),可根據業務需求調整為保留首條或合併為數組。
「以空間換時間」是經典優化策略。將關聯數組轉為鍵值索引映射,避免了重複遍歷,代碼簡潔且性能穩健。下次遇到數組 JOIN 需求,請優先考慮此模式。