JavaScript检测嵌套对象子属性重复项并标记error的实现方法
姓名重复校验实现方案
你原有代码存在几个可优化点和问题:
- 字段笔误:遍历儿童列表时写的是
room.childs,实际数据结构中对应字段为children,会导致遍历不到儿童数据 - 性能问题:每次判断重复都全量遍历已存姓名数组,时间复杂度为O(n²),数据量大时运行效率低
- 逻辑偏差:原有逻辑仅标记第一个出现姓名之后的重复项,和你给出的期望输出(所有同名重复项均标记)不匹配
- 冗余字段:无错误的人员会被赋值为空字符串的
error属性,和期望输出结构(无错人员无error字段)不符 - 残留问题:用户修改姓名后重新校验时,旧的错误标记不会被清除,容易出现误判
优化后实现代码
核心思路是分两次遍历:第一次统计所有姓名组合的出现频次,第二次根据频次给对应人员加/删错误标记,整体时间复杂度O(n),性能更好,逻辑更清晰:
const validateNames = (rooms) => { const nameCountMap = new Map(); // 第一次遍历:统计每个 名+姓 组合的出现次数 rooms.forEach(room => { // 合并成人、儿童数组统一处理,减少重复代码 [...room.adults, ...room.children].forEach(person => { // 用特殊分隔符拼接名和姓作为key,避免名/姓本身的字符导致key冲突,加trim处理首尾空格 const nameKey = `${person.first.trim()}|${person.last.trim()}`; nameCountMap.set(nameKey, (nameCountMap.get(nameKey) || 0) + 1); }); }); // 第二次遍历:标记重复项,清除非重复项的旧错误标记 rooms.forEach(room => { [...room.adults, ...room.children].forEach(person => { const nameKey = `${person.first.trim()}|${person.last.trim()}`; if (nameCountMap.get(nameKey) > 1) { person.error = "Duplicate name, please update."; } else { delete person.error; } }); }); };
Vue3 中使用方式
因为你是接收的prop数据,只修改人员对象的内部属性不会违反Vue单向数据流规则,响应式可以正常追踪,配合watch可以实现数据变化自动校验:
import { watch, defineProps } from 'vue'; const props = defineProps({ rooms: { type: Array, required: true } }); // 深度监听rooms数据,初始化和数据变化时自动执行校验 watch( () => props.rooms, () => validateNames(props.rooms), { deep: true, immediate: true } );
补充说明
- 代码中加了
trim()处理首尾空格,可以避免用户输入时不小心打了多余空格,导致内容相同的姓名被判定为不重复的问题 - 如果你实际需求是「仅标记第一个出现姓名之后的重复项,第一个出现的不标记」,只需要把第一次遍历的逻辑改成记录已出现的key,第二次遍历时遇到已存在的key就标记,未存在的就存入key即可
- 不要直接替换整个rooms数组或者room对象,仅修改内部person的属性,不会触发Vue的prop修改警告
内容的提问来源于stack exchange,提问作者Sheehab Muhammad
相关产品推荐
相关产品推荐

