Google Sheets脚本实现赛事配对:解决空白与同行重复问题
Google Sheets 赛事对阵配对脚本调试方案
需求说明
- 利用Google Sheets脚本生成赛事对阵配对:首列为选手名单,后续列对应各轮次的对手列表
- 必须过滤空白单元格,防止排序后出现空白行
- 确保每行中选手姓名与对手姓名不重复,若出现重复则重新打乱数组直至符合要求
遇到的问题
使用filter(String)过滤空白单元格后,执行setValues时触发数据行数与目标范围行数不匹配的错误,无法彻底消除空白区域。后续将代码迁移至测试表并添加两轮配对逻辑,但仍存在需要调试的问题。
初始代码
function shuffleRange() { var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP'); var range = sheet.getRange('A31:A') var range2 = sheet.getRange('C31:C'); range2.clearContents; range2.setValues(shuffleArray(range.getValues())); } function shuffleArray(array) { var i, j, temp; for (i = array.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i+1)); temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; }
更新后的测试代码
注:代码已迁移至测试表,对应工作表名称和范围已调整
function shuffleRange() { var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet4'); var range = sheet.getRange('A1:A40') var v = range.getValues().filter(String); //Match 1 var values = shuffleArray1(v); while (v.length != [...new Set(values.map(([a]) => a))].length) { values = shuffleArray1(v); } range.offset(0, 1, values.length).setValues(values); //Match 2 var values2 = shuffleArray2(v); while (v.length != [...new Set(values2.map(([a]) => a))].length) { values = shuffleArray2(v); } range.offset(0, 2, values.length).setValues(values2); } function shuffleArray1(array) { var i, j, temp; for (i = array.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i+1)); temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } function shuffleArray2(array) { var u, v, temp; for (u = array.length - 3; u > 0; u--) { v = Math.floor(Math.random() * (u+2)); temp = array[u]; array[u] = array[v]; array[v] = temp; } return array; }
问题修复与优化方案
1. 解决setValues行数不匹配问题
原代码中range.getValues()会获取整列所有单元格(含大量空白行),过滤后数组长度远小于原范围行数,导致setValues报错。正确做法:
- 过滤空白后,仅针对有效数据的范围写入值
- 写入前清空目标列中对应有效数据行的内容,而非整列
2. 修复重复姓名判断逻辑
原代码判断逻辑错误,当前是检查打乱后的数组是否有重复选手,实际需求是每行的选手与对应对手姓名不重复,需改为逐行对比原数组和打乱后的数组,确保原选手[i] !== 对手[i]。
3. 优化洗牌函数
无需定义多个洗牌函数,一个通用函数即可满足多轮配对需求,减少代码冗余。
优化后的完整代码
function generateMatchups() { const sheet = SpreadsheetApp.getActive().getSheetByName('Sheet4'); // 获取首列数据并严格过滤空白 const rawData = sheet.getRange('A1:A40').getValues(); const players = rawData.filter(row => row[0].toString().trim() !== ''); const playerCount = players.length; if (playerCount === 0) return; // 通用洗牌函数(创建数组副本,不修改原数据) function shuffleArray(arr) { const newArr = [...arr]; for (let i = newArr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [newArr[i], newArr[j]] = [newArr[j], newArr[i]]; } return newArr; } // 生成符合要求的对阵列表 function generateValidMatchup(baseArr) { let matchup; do { matchup = shuffleArray(baseArr); // 逐行检查是否无重复姓名 let isValid = true; for (let i = 0; i < playerCount; i++) { if (baseArr[i][0] === matchup[i][0]) { isValid = false; break; } } if (isValid) break; } while (true); return matchup; } // 写入第一轮对阵到B列 const matchup1 = generateValidMatchup(players); sheet.getRange(1, 2, playerCount, 1).clearContent().setValues(matchup1); // 写入第二轮对阵到C列 const matchup2 = generateValidMatchup(players); sheet.getRange(1, 3, playerCount, 1).clearContent().setValues(matchup2); }
代码说明
- 过滤空白时去除首尾空格,避免误过滤含空格的无效单元格
- 洗牌操作创建数组副本,不破坏原选手列表
- 有效性检查精准匹配需求,确保每行选手与对手姓名不重复
- 仅针对有效选手数量的范围写入数据,彻底解决行数不匹配问题
内容的提问来源于stack exchange,提问作者Tweak
相关产品推荐
相关产品推荐

