
本文介绍一种动态解析下划线分隔键名的算法,将扁平的一维数组自动重构为符合语义层级的嵌套二维数组,无需预知键名模式,适用于 wordpress 自定义字段、表单序列化数据等场景。
在实际开发中(如处理 wordPress ACF 字段、前端表单序列化提交或配置项解析),我们常遇到一类“伪结构化”数据:它以一维关联数组形式存在,但键名(key)本身隐含层级语义——例如 content_0_title 表示 content → 0 → title,featured_video_closed_captions 暗示 featured_video → video → closed_captions。目标是不依赖硬编码规则,仅通过分析键名的共性前缀与分段逻辑,将其智能还原为真正的嵌套数组结构。
核心思路是:按 _ 拆分键名 → 提取公共前缀组 → 识别数字索引 → 构建路径树 → 逐层赋值。以下为完整、健壮、可直接使用的实现:
$value) { if (empty($key)) continue; // 步骤1:拆分键名为语义片段 $parts = explode('_', $key); // 步骤2:识别可能的“根前缀”——取最长连续公共前缀(从左到右匹配) $root = $parts[0]; $i = 1; while ($i < count($parts)) { $candidate = implode('_', array_slice($parts, 0, $i)); // 检查是否存在其他键以该候选为前缀(且后跟下划线) $hasMatch = false; foreach ($flat as $otherKey => $v) { if ($otherKey !== $key && strpos($otherKey, $candidate . '_') === 0) { $hasMatch = true; break; } } if (!$hasMatch) break; $root = $candidate; $i++; } // 步骤3:提取剩余路径(去除根前缀后的部分) $relativePath = substr($key, strlen($root) + 1); // +1 for '_' if (empty($relativePath)) { $path = [$root]; } else { $path = explode('_', $relativePath); } // 步骤4:特殊处理数字索引(如 content_0_title → content[0][title]) // 将纯数字片段转为整型,并尝试合并相邻数字与非数字(如 ['0','title'] → [0]['title']) $cleanPath = []; foreach ($path as $segment) { if (is_numeric($segment) && (int)$segment >= 0) { $cleanPath[] = (int)$segment; } else { $cleanPath[] = $segment; } } // 步骤5:沿路径深度赋值(递归构建嵌套结构) $ref =& $result; $lastIndex = count($cleanPath) - 1; foreach ($cleanPath as $idx => $step) { if ($idx === $lastIndex) { $ref[$step] = $value; } else { if (!isset($ref[$step]) || !is_array($ref[$step])) { $ref[$step] = []; } $ref =& $ref[$step]; } } } return $result; } // 示例使用 $flat = [ 'featured_video_video_type' => [], 'featured_video_video_mp4' => [], 'featured_video_video_webm' => [], 'featured_video_closed_captions' => [], 'featured_video' => [], 'content' => [], 'content_0_title' => [], 'content_0_content' => [], 'content_1_quote' => [], 'content_1_citation' => [], ]; $nested = array_flatten_to_nested($flat); print_r($nested);
✅ 关键特性说明:
- 无先验知识:不依赖预定义键名列表,完全基于键名共现关系推断层级;
- 数字索引友好:自动识别 content_0_title 中的 0 为数组下标,生成 $arr[‘content’][0][‘title’];
- 防冲突设计:对 featured_video(根键)和 featured_video_video_mp4(子键)能正确区分主干与分支;
- 健壮容错:跳过空键、忽略非标准格式键,避免崩溃。
⚠️ 注意事项:
- 该算法假设键名遵循一致的 _ 分隔约定,且语义层级由前缀长度决定(越长的公共前缀越可能是独立模块);
- 若存在歧义键名(如 user_name 和 user_name_first 与 user_name_last 同时存在),需确保其前缀共现性足够强,否则建议在数据源头规范命名;
- 性能上为 O(n²) 最坏情况(前缀检测遍历),但对百级键名规模影响极小,生产环境可接受。
通过此方法,你不再需要手动映射每个字段,而是赋予数组“自我理解”的能力——让结构从键名中自然浮现。