
本文详解如何正确实现递归函数,检测 questionmodel 与 responsemodel 交错嵌套结构中同一层级内 label 属性的重复值,修复原逻辑中因过早 return 导致的递归中断问题。
本文详解如何正确实现递归函数,检测 questionmodel 与 responsemodel 交错嵌套结构中同一层级内 label 属性的重复值,修复原逻辑中因过早 return 导致的递归中断问题。
在处理具有双向嵌套关系的 JSON 数据(如 QuestionModel 包含 responses 数组,而 ResponseModel 又包含 questions 数组)时,常见的错误是在递归调用后立即 return,导致函数未遍历完当前层级所有对象,就提前终止——这正是原始代码始终返回 false 的根本原因。
原始代码的关键缺陷在于:
if ("questions" in obj) return await duplicatedLabel(obj.questions!);
if ("responses" in obj) return await duplicatedLabel(obj.responses!);
一旦遇到第一个含 questions 的对象,函数立刻返回其子树检查结果,后续同级对象(如第二个 "label": "yes")被完全跳过,无法参与当前层级的去重校验。
✅ 正确做法是:先完成当前层级所有对象的 label 去重检查,再逐个递归检查子结构,并汇总子树结果。仅当子树中任一路径发现重复时,才向上透传 true。
以下是修正后的完整实现:
type ResponseModel = {
label: string;
questions?: QuestionModel[];
};
export type QuestionModel = {
label: string;
responses?: ResponseModel[];
};
const duplicatedLabel = async (data: QuestionModel[] | ResponseModel[]): Promise<boolean> => {
const labelSet = new Set<string>();
for (const obj of data) {
// 检查当前层级 label 是否重复
if (labelSet.has(obj.label)) {
return true; // 当前层已存在重复,立即返回
}
labelSet.add(obj.label);
// 递归检查子结构,但不立即 return!需继续遍历同级其他对象
let hasDuplicateInSubtree = false;
if ("questions" in obj && obj.questions?.length) {
hasDuplicateInSubtree = await duplicatedLabel(obj.questions);
}
if (!hasDuplicateInSubtree && "responses" in obj && obj.responses?.length) {
hasDuplicateInSubtree = await duplicatedLabel(obj.responses);
}
if (hasDuplicateInSubtree) {
return true; // 子树发现重复,整体返回 true
}
}
return false; // 当前层及所有子树均无重复
};</string></boolean>
? 关键改进点说明:
- 使用
const obj of data替代let obj of data(语义更清晰,无需修改循环变量); - 显式添加
obj.questions?.length和obj.responses?.length安全判断,避免空数组引发无效递归; - 将子树检查结果暂存为
hasDuplicateInSubtree,仅在确认为true时才return true,确保同级所有对象都被遍历; - 类型标注
Promise<boolean></boolean>提升可读性与类型安全。
? 使用示例:
对题干中含两个 "yes" label 的 JSON 输入,该函数将准确返回 true —— 因为在 do you have a visa? 节点的 responses 数组中,两个子对象 label 均为 "yes",触发同层重复检测。
⚠️ 注意事项:
- 该函数按「深度优先 + 同层先行」策略执行,时间复杂度为 O(N),其中 N 为所有节点总数;
- 若需获取具体重复的 label 值或位置路径,可扩展为返回
{ found: boolean; label?: string; path?: string[] }类型; - 在大型数据集上,可考虑加入递归深度限制或
AbortSignal支持,防止栈溢出或无限循环。
通过重构控制流逻辑,我们让递归真正服务于层级校验意图:每一层独立去重,每一棵子树独立验证,全局结果由最浅层首次命中决定——这才是嵌套结构重复检测的健壮范式。











