將任意深度的多維陣列「拍平」為一維陣列是常見需求。本文對比三種遞歸寫法——foreach(通用)、for(僅數字索引)、while + each()(指針操作),分析適用場景與效能差異。
function arr_foreach($arr) {
static $tmp = [];
if (!is_array($arr)) return false;
foreach ($arr as $val) {
if (is_array($val)) {
arr_foreach($val);
} else {
$tmp[] = $val;
}
}
return $tmp;
}
// 測試
$a = [1, 2 => [3, 4 => [5, 6]], 7];
print_r(arr_foreach($a));
/*
Array
(
[0] => 1
[1] => 3
[2] => 5
[3] => 6
[4] => 7
)
*/
優點:支援關聯陣列與數字索引混合、語義清晰、PHP 所有版本相容。
缺點:靜態變數導致非線程安全、多次呼叫需手動重置。
function arr_for($arr) {
static $tmp = [];
for ($i = 0; $i < count($arr); $i++) {
if (is_array($arr[$i])) {
arr_for($arr[$i]);
} else {
$tmp[] = $arr[$i];
}
}
return $tmp;
}
$a = [1, [3, [5, 6]], 7];
print_r(arr_for($a)); // 同樣輸出 [1,3,5,6,7]
限制:count($arr) 與 $arr[$i] 僅適用於連續數字鍵,關聯鍵會被跳過或報錯。
/**
* 多維轉一維(指針操作版)
* @param array $arr
* @return array
*/
function ArrMd2Ud($arr) {
// 以第一個元素為基準容器(引用賦值)
$ar_room = &$arr[key($arr)];
if (!is_array($ar_room)) $ar_room = [$ar_room];
next($arr); // 指針下移
while (list($k, $v) = each($arr)) {
// 遞歸處理子陣列,非陣列包裝為陣列
$v = is_array($v) ? call_user_func(__FUNCTION__, $v) : [$v];
// 遞歸合併
$ar_room = array_merge_recursive($ar_room, $v);
// 釋放已處理元素
unset($arr[$k]);
}
return $ar_room;
}
$arr = [1, 2, 3 => [1, 2, 'ar' => [1, 2 => ['a', 'b']]], ['ar' => [3, 4]]];
print_r(ArrMd2Ud($arr));
/*
Array
(
[0] => 1
[1] => 2
[2] => 1
[3] => 2
[4] => 1
[5] => a
[6] => b
[7] => 3
[8] => 4
)
*/
特點:利用引用與指針原地操作,記憶體效率較高;但 each() 在 PHP 7.2+ 棄用、PHP 8.0+ 移除,現代專案不建議使用。
function flatten_modern($arr) {
$result = [];
array_walk_recursive($arr, function($val) use (&$result) {
$result[] = $val;
});
return $result;
}
| 方法 | 支援關聯鍵 | PHP 版本 | 效能 | 推薦度 |
|---|---|---|---|---|
| foreach + static | 是 | 所有 | 中 | ★★★☆☆ |
| for 循環 | 否 | 所有 | 中 | ★★☆☆☆ |
| while + each() | 是 | <7.2 | 較高 | ★☆☆☆☆ (棄用) |
| array_walk_recursive | 是 | 5.0+ | 最高 | ★★★★★ |
array_walk_recursive 或 RecursiveArrayIterator + RecursiveIteratorIterator。user.address.city)。扁平化看似簡單,選對工具能省下大量維護成本,現代 PHP 開發者應優先採用內建遞歸迭代器。