Google Apps Script含嵌套IF的FOR循环迭代异常报错排查
学生志愿群组分配脚本故障修复
问题概述
编写的脚本用于按先到先得规则,基于学生提交的排序志愿数据完成群组分配。脚本初始设计逻辑为通过两层For循环构建响应处理顺序:按提交顺序选中第一位学生,依次遍历其第1、2、3个志愿,匹配志愿对应的群组;若目标群组未达容量上限,则将该学生划入对应群组,完成后处理下一位学生的三个志愿。
异常现象
- 初始版本脚本必须手动添加
i++循环迭代触发语句才可正常运行,按照循环设计逻辑,满足判定条件后循环应自动完成迭代,移除该行i++后脚本逻辑完全失效。 - 手动添加
i++会触发边界问题:当计数器i等于respCount(学生响应总数)时,分配完最后一位学生后i++仍会累加计数,尝试读取responses数组边界外的数据,抛出如下报错:
TypeError: Cannot read property '0' of undefined
已尝试方案
为规避上述报错,曾在循环内添加响应数组undefined校验逻辑,命中空值时直接终止循环,但该方案仅掩盖了越界问题,未从逻辑根源修复缺陷。
测试数据结构样例
| Names | Pref1 | Pref2 | Pref3 |
|---|---|---|---|
| Zi Yang | R | T | E |
| Reeva Dalton | T | Q | E |
| Keagan Mcguire | T | Y | R |
| Randy Robins | E | R | W |
问题根因
学生分配成功后未正确跳出多层嵌套循环,开发者为了推进流程手动修改外层循环计数器,最终导致数组越界报错。
初始问题代码
function assignGroups() { const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data") // 选择数据所在工作表 /** 获取数据范围 */ const respCount = ss.getRange("A2:A").getValues().filter(String).length // 待处理的响应总数 const responses = ss.getRange("A2:D"+(respCount+1)).getValues() // 所有学生及志愿数据数组,+1是为了适配表头行的偏移 const choices = 3 // 学生可填报的最大志愿数,需根据实际数据调整 /** 获取群组信息 */ const deptCount = ss.getRange("F2:F").getValues().filter(String).length // 统计可选群组总数 const departments = ss.getRange("F2:G"+(deptCount+1)).getValues() // 获取群组ID及对应容量数组,+1是为了适配表头行的偏移 /** 遍历处理学生志愿 */ for (var i = 0; i < respCount; i++){ // 从第一行开始遍历学生响应数组 for (var j = 1; j <= choices; j++){ // 从第一志愿开始遍历当前学生的所有志愿 if (responses[i] == undefined){ // 校验计数器是否在数组范围内 break; // 计数器越界时直接终止循环,避免读取空值报错 } var studentName = responses[i][0] // 获取当前学生姓名 var studentChoice = responses[i][j] // 获取当前遍历到的学生志愿 /** 遍历匹配群组信息 */ for (var k = 0; k < deptCount; k++){ // 遍历群组数组 var depID = departments[k][0] // 获取群组ID var depSize = departments[k][1] // 获取群组容量 /** 执行分配逻辑 */ if (studentChoice == depID){ // 学生志愿与群组ID匹配时 var depSpace = ss.getRange("I2:I").offset(0,k).getValues().filter(String).length // 统计当前群组已分配的学生人数 if (depSpace < depSize){ // 群组还有剩余容量时 ss.getRange("I2").offset(depSpace,k).setValue(studentName); // 将学生姓名写入对应群组的分配列 i++;// 手动推进到下一位学生,该行是引发故障的核心问题点 j=0;// 重置志愿计数器 } } } } } }
修复方案
核心修复逻辑:
- 移除手动篡改外层学生遍历计数器
i的错误代码,完全交由外层for循环自带的自增逻辑控制遍历节奏,从根源避免数组越界 - 学生成功分配到群组后,将志愿遍历计数器
j设为循环终止阈值,配合break跳出部门匹配的最内层循环,自动进入下一位学生的分配流程 - 优化数据读取逻辑,直接过滤空行获取有效学生响应、部门数据,无需手动计算行数拼接数据范围,减少范围计算错误
修复后代码(2022/07/02 更新)
function assignDepartment() { const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data") /** 获取数据范围 */ const responses = ss.getRange("A2:D").getValues().filter(row => row[0] !== ""); const respCount = responses.length const choices = 3 /** 获取群组信息 */ const departments = ss.getRange("F2:G").getValues().filter(row => row[0] !== ""); const deptCount = departments.length /** 遍历处理学生志愿 */ for (let i = 0; i < respCount; i++) { for (let j = 1; j <= choices; j++) { var studentName = responses[i][0] var studentChoice = responses[i][j] /** 遍历匹配群组信息 */ for (let k = 0; k < deptCount; k++) { var depID = departments[k][0] var depSize = departments[k][1] /** 执行分配逻辑 */ if (studentChoice == depID) { var depSpace = ss.getRange("I2:I").offset(0,k).getValues().filter(String).length if (depSpace < depSize) { ss.getRange("I2").offset(depSpace,k).setValue(studentName); j = choices+1; break; } } } } } }
内容的提问来源于stack exchange,提问作者mss_
相关产品推荐
相关产品推荐

