
本文介绍一种递归遍历 QuestionModel 与 ResponseModel 双重嵌套结构的方法,自动为每个节点生成基于路径的唯一 id,确保父子关系可追溯、语义清晰且无冲突。
本文介绍一种递归遍历 `questionmodel` 与 `responsemodel` 双重嵌套结构的方法,自动为每个节点生成基于路径的唯一 `id`,确保父子关系可追溯、语义清晰且无冲突。
在构建动态问答系统(如多级表单、决策树或对话流程)时,常需为每个问题(QuestionModel)和答案(ResponseModel)分配唯一标识符(id),以支持前端状态管理、后端存储映射或分析追踪。由于结构深度不确定且存在双向嵌套(QuestionModel 包含 responses,ResponseModel 包含 questions),必须采用深度优先的递归策略,并精确维护当前路径上下文。
✅ 核心思路:路径继承 + 标签规范化
每个节点的 id 由其父节点 ID + 当前 label 规范化后缀拼接而成。关键处理包括:
- 使用空字符串作为根节点初始路径;
- 对
label执行.replace(/\s+/g, '')去除所有空白字符(适配"do you have a visa?"→"doyouhaveavisa?"); - 利用
'questions' in data和'responses' in data类型守卫安全访问可选嵌套字段; - 递归调用时传入已生成的
data.id作为子节点路径前缀。
? 实现代码(TypeScript)
type ResponseModel = {
label: string;
questions?: QuestionModel[];
id?: string;
};
type QuestionModel = {
label: string;
responses?: ResponseModel[];
id?: string;
};
const identify = (
data: QuestionModel | ResponseModel,
path = ''
): QuestionModel | ResponseModel => {
// 生成当前节点 ID:父路径 + '_'(若非根) + 规范化 label
const cleanLabel = data.label.replace(/\s+/g, '');
data.id = path + (path ? '_' : '') + cleanLabel;
// 递归处理子 questions(仅 QuestionModel 有)
if ('questions' in data && Array.isArray(data.questions)) {
data.questions.forEach(q => identify(q, data.id));
}
// 递归处理子 responses(仅 ResponseModel 有)
if ('responses' in data && Array.isArray(data.responses)) {
data.responses.forEach(r => identify(r, data.id));
}
return data;
};
// 使用示例
const originalData: QuestionModel = {
label: "your destination?",
responses: [
{
label: "USA",
questions: [
{
label: "do you have a visa?",
responses: [
{ label: "yes" },
{ label: "no" }
]
}
]
},
{
label: "Canada",
questions: [
{
label: "do you have a work licence?",
responses: [
{ label: "yes" },
{ label: "no" }
]
}
]
}
]
};
const hydratedData = identify(originalData);
console.log(JSON.stringify(hydratedData, null, 2));
⚠️ 注意事项与最佳实践
-
不可变性考虑:上述实现直接修改原对象。如需保持不可变性,应创建新对象副本(例如使用结构展开
{ ...data, id: ... }),并在递归中返回新实例; -
特殊字符鲁棒性:当前仅移除空白符,若
label含/,?,#等 URL/JSON 不友好字符,建议追加encodeURIComponent()或正则替换(如.replace(/[^a-zA-Z0-9]/g, '')); -
ID 冲突预防:确保同一层级的
label不重复(如两个"yes"),否则将产生相同id;业务层应在数据录入时校验或添加序号后缀(如_yes_1,_yes_2); - 性能提示:对超深嵌套(>100 层)或极大数组(>10k 节点),可改用栈式迭代避免调用栈溢出,但绝大多数问答配置场景中递归完全适用。
通过该方案,你可一键为任意复杂度的嵌套问答 JSON 注入语义化、可追溯、层级明确的 id,为后续交互逻辑、持久化或分析打下坚实基础。











