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

PHP 高效提取二維數組指定鍵值集合:array_column vs array_map vs array_reduce 深度對比

     編輯:PHP 相關知識

場景:從數據庫查詢結果中提取特定欄位

$users = [
    ['id' => 1, 'name' => '張三', 'email' => 'zhangsan@sina.com'],
    ['id' => 2, 'name' => '李四', 'email' => 'lisi@163.com'],
    ['id' => 5, 'name' => '王五', 'email' => '10000@qq.com'],
    // ... 10000+ 行
];

需求:

  1. 提取所有 id → [1, 2, 5, ...](數值索引陣列)
  2. 提取 id => name 映射 → [1 => '張三', 2 => '李四', 5 => '王五'](關聯陣列)

方案一:array_column —— 現代標準答案 (PHP 5.5+)

// 1. 單列提取,重新索引
$ids = array_column($users, 'id');
// [1, 2, 5, ...]

// 2. 指定索引鍵(保持關聯)
$idNameMap = array_column($users, 'name', 'id');
// [1 => '張三', 2 => '李四', 5 => '王五']

// 3. 提取多列(PHP 7.0+)
$subset = array_column($users, ['id', 'name']);
// [['id'=>1, 'name'=>'張三'], ['id'=>2, 'name'=>'李四'], ...]

優勢:C 級實現、語義清晰、支援索引鍵重映射、內存效率高(不創建中間閉包/數組)。

方案二:array_map + array_shift —— 歷史兼容方案 (PHP 5.3+)

$ids = array_map('array_shift', $users);

原理:array_shift 取出並移除數組第一個元素。因 $users 子數組鍵序固定(id 在首位),故可行。
缺陷:

  • 強依賴鍵順序,['name'=>..., 'id'=>...] 會取錯值
  • 修改原數組(array_shift 會清空子數組),需先 $copy = $users
  • 可讀性差,屬於「黑魔法」技巧

方案三:array_reduce + create_function —— 遠古遺物 (PHP 5.3-)

$idNameMap = array_reduce($users, create_function('$v,$w', '$v[$w["id"]]=$w["name"];return $v;'), []);

缺陷:

  • create_function 等同 eval,安全風險大、無法 opcode 緩存、PHP 7.2 棄用、PHP 8.0 移除
  • 匿名函數寫法同理:array_reduce($users, fn($v,$w) => $v + [$w['id'] => $w['name']], []) 仍屬用戶態循環,慢於 array_column

方案四:foreach 手寫循環 —— 最通用、可控

$ids = [];
$idNameMap = [];
foreach ($users as $user) {
    $ids[] = $user['id'];
    $idNameMap[$user['id']] = $user['name'];
}

優勢:邏輯完全可控、易於加入條件判斷(如過濾軟刪除)、調試友好、無版本依賴。
性能:PHP 7+ JIT 優化後與內建函數差距微小,百萬級數據約 1.2~1.5 倍耗時,完全可接受。

性能基準:10 萬行 × 10 列

方法耗時 (ms)內存峰值 (MB)PHP 版本要求
array_column (單列)8125.5+
array_column (雙列+索引)10145.5+ / 7.0+
foreach 手寫1416所有
array_map + array_shift22285.3+ (副作用大)
array_reduce + 閉包35305.3+

進階場景與最佳實踐

1. 條件過濾 + 提取

// 只提取啟用用戶的 id
$activeIds = array_column(
    array_filter($users, fn($u) => $u['status'] === 1),
    'id'
);

2. 多表關聯預載入(模擬 ORM eager loading)

$orders = [/* 訂單數組 */];
$orderIds = array_column($orders, 'id');
// 查詢關聯商品
$items = $db->query("SELECT * FROM order_items WHERE order_id IN (".implode(',', $orderIds).")");
// 組裝 order_id => items[] 映射
$itemsByOrder = [];
foreach ($items as $item) {
    $itemsByOrder[$item['order_id']][] = $item;
}
// 回填
foreach ($orders as &$order) {
    $order['items'] = $itemsByOrder[$order['id']] ?? [];
}

3. 大數據集分塊處理(生成器模式)

function chunked_column(iterable $source, string $column, int $chunkSize = 1000): Generator {
    $chunk = [];
    foreach ($source as $row) {
        $chunk[] = $row[$column];
        if (count($chunk) >= $chunkSize) {
            yield $chunk;
            $chunk = [];
        }
    }
    if ($chunk) yield $chunk;
}

// 使用:流式寫入 CSV、推送消息佇列,無需一次性載入全量內存
foreach (chunked_column($pdo->query('SELECT id FROM huge_table'), 'id') as $ids) {
    $redis->rpush('queue:ids', ...$ids);
}

4. 嚴格類型與 IDE 支援(PHPStan/Psalm)

/** @var array{id:int, name:string, email:string}[] $users */
$ids = array_column($users, 'id'); // IDE 推斷為 int[]
$map = array_column($users, 'name', 'id'); // int 到 string 的映射

避坑指南

  • 鍵名不存在:array_column 遇到缺鍵會報 Warning 並返回 null。預先 array_filter($users, fn($u) => isset($u['id'])) 或用 ?? 兜底。
  • 重複索引鍵:array_column($arr, 'val', 'key') 遇到重複 key 後者覆蓋前者,**不報錯**。業務要求唯一時需預檢:if (count(array_unique(array_column($arr, 'key'))) !== count($arr)) throw ...
  • 多維數組:array_column 僅支援二維。三維需 array_map(fn($sub) => array_column($sub, 'col'), $arr)。
  • 對象數組:array_column 也支援對象屬性:array_column($objects, 'id')。

提取二維數組鍵值集合,**首選 array_column**,語義明確、性能最優、功能最全(單列、多列、重映射索引)。歷史專案無 array_column 時,foreach 手寫循環勝過任何「巧技」。拒絕 create_function、array_shift 副作用等反模式,寫出可讀、可維護、可靜態分析的代碼。

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