array_map最适合遍历修改多维数组每一行,返回新数组而不改变原数组;需接收返回值,PHP 7.4+可用箭头函数;就地修改须用foreach &$row并unset($row);嵌套深层需递归或嵌套循环;注意键名保留与重索引需求。

用 array_map 遍历修改多维数组的每一行
直接对多维数组的“每一行”(即每个子数组)做统一处理,array_map 是最贴切的选择。它天然接收一个回调函数和一个数组,对数组每个元素(这里是每行子数组)调用该函数,并返回新数组。
注意:它不改变原数组,而是生成新数组——这点常被忽略,导致误以为没生效。
- 如果原数组是
$data = [['name' => 'a'], ['name' => 'b']],想给每行加'status' => 'active',写法是:$newData = array_map(function($row) { $row['status'] = 'active'; return $row; }, $data); - 不能写成
array_map(..., $data)后不接赋值——返回值必须被接收,否则丢弃 - PHP 7.4+ 可用箭头函数简化:
array_map(fn($r) => $r + ['status' => 'active'], $data),但注意+是键合并,不是覆盖;若需强制覆盖已有键,仍建议用赋值方式
用 foreach 引用修改原数组(不生成新数组)
当必须就地修改原数组(比如函数内要影响外部变量),就得用 foreach 并带上引用符号 &。漏掉 & 是最常见错误,结果看起来“没改成功”。
- 正确写法:
foreach ($data as &$row) { $row['updated_at'] = date('Y-m-d H:i:s'); } unset($row); // 必须加!否则下一次循环或后续代码可能意外修改最后一个元素 -
unset($row)不是可选项——PHP 的引用变量会持续存在,后续对$row的任何操作(比如另一次foreach)都会改到原数组最后一项 - 如果只是读取+计算、不改结构,不用引用;一旦涉及
$row['key'] = ...这类赋值,就必须用&$row
嵌套更深时,递归修改容易出错
如果“每一行”里还嵌套了数组(比如 ['user' => ['id' => 1, 'profile' => [...]]]),而你想统一处理所有最内层的某个字段(如所有 id 加前缀),就不能只靠一层 array_map 或 foreach。
立即学习“PHP免费学习笔记(深入)”;
- 简单场景:明确知道嵌套层级,比如固定两层,可嵌套遍历:
foreach ($data as &$row) { if (isset($row['meta']) && is_array($row['meta'])) { foreach ($row['meta'] as &$item) { $item['processed'] = true; } unset($item); } } unset($row); - 通用场景:不确定深度,需递归函数。但要注意——递归中修改引用容易引发意外,更稳妥的是用递归 + 返回新数组,避免副作用
- 性能提醒:递归处理大数组(>1000 行)可能触发 PHP 默认的 100 层递归限制,需检查
xdebug.max_nesting_level或改用栈模拟
批量修改后保留键名还是重索引?
array_map 会保留原始键名(包括字符串键),而 foreach 引用修改不影响键名本身。但如果你中间用了 array_values() 或 array_filter() 等函数,键名可能丢失或错乱。
- 需要连续数字索引(如 JSON 输出要求):最后加
$newData = array_values($newData) - 原数组是关联键(如
['user_123' => [...], 'user_456' => [...]]),且业务依赖这些键——千万不能用array_values,否则查不到对应记录 -
array_filter默认保留键名,但若你写了array_filter($data, $callback, ARRAY_FILTER_USE_BOTH)并做了键名判断,也要同步确认是否需重索引
真正麻烦的不是怎么改,而是改完之后谁在用这个数组——键名语义、JSON 序列化表现、数据库插入逻辑,都得对得上。动之前先看一眼 var_dump(array_keys($data))。



















