如何在Google Apps Script中用map()高效查找空行并补全数据
优化Google Apps Script数据匹配效率:替代for循环+使用map()或更优方案
我刚接触map()函数,目前用for循环处理大量数据时速度太慢,想改用map()替代原有的for和if逻辑,或者找其他更高效的优化方案。需要实现以下流程:
- 从Data数据表中找出第3列为空的行;
- 将该行的列1和列2的值合并;
- 在Source_data数据源表中查找列1与该合并值匹配的行;
- 若找到匹配行,提取其列2、列3、列4的数据;
- 将提取的数据写入Data表对应行的列3、列4、列5,达到预期输出效果。
Data数据表
| 列1 | 列2 | 列3 | 列4 | 列5 |
|---|---|---|---|---|
| lose | data | data1 | data2 | data3 |
| Second | row | |||
| Second | row | |||
| Second | row | data4 | data5 | data6 |
Source_data数据源表
| 列1 | 列2 | 列3 | 列4 |
|---|---|---|---|
| losedata | data1 | data2 | data3 |
| Secondrow | data4 | data5 | data6 |
预期输出Data表
| 列1 | 列2 | 列3 | 列4 | 列5 |
|---|---|---|---|---|
| lose | data | data1 | data2 | data3 |
| Second | row | data4 | data5 | data6 |
| Second | row | data4 | data5 | data6 |
| Second | row | data4 | data5 | data6 |
当前代码
function main3() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheet = ss.getSheetByName('Sheet1'); var data = sheet.getDataRange().getValues(); var source_file = SpreadsheetApp.openById("").getSheetByName(""); var source_data = source_file.getRange(2, 1, source_file.getLastRow() - 1, source_file.getLastColumn()).getValues(); var headerName1 = "Column 1"; var headerName2 = "Column 2"; var header = data.shift(); var header_index1 = header.indexOf(headerName1); var header_index2 = header.indexOf(headerName2); // 查找第3列为空的行 for (var i = 1; i < data.length; i++) { if (data[i][2] == "") { // 合并两列 // 列1: lose 和 列2: data // concat变量会生成合并后的值 -> losedata var concat = data.map((row, i) => ([data[i][header_index1] + data[i][header_index2]])); // 在source_data中查找与合并值匹配的行 // 比如查找"losedata"这样的匹配数据 var matching = concat.map(row => { var row_match = source_data.find(r => r[0] == row[0]) return row_match ? [row_match[3], row_match[4], row_match[5]] : [null, null, null] }); // 将值写入Data表 sheet.getRange(2, 3, matching.length, matching[0].length).setValues(matching); } } }
优化方案(高效版)
核心优化点
- 预构建Source数据映射表:把Source_data的列1作为键,对应的列2-4数据作为值,查找匹配值的时间复杂度从O(n)降到O(1);
- 批量处理数据:一次性处理所有需要更新的行,只进行一次数据写入操作,减少Spreadsheet API调用次数(这是提升脚本速度的关键);
- 合理使用
map():用数组方法批量遍历处理,替代嵌套循环,代码更简洁高效。
优化后代码
function optimizedMain() { // 获取当前表格和数据 const ss = SpreadsheetApp.getActiveSpreadsheet(); const dataSheet = ss.getSheetByName('Sheet1'); const data = dataSheet.getDataRange().getValues(); const header = data[0]; // 保留表头,避免索引混乱 // 替换为你的Source文件ID和表名 const sourceFile = SpreadsheetApp.openById("你的Source文件ID").getSheetByName("Source_data"); const sourceData = sourceFile.getDataRange().getValues(); // 1. 构建Source数据映射表:合并值 -> [列2, 列3, 列4] const sourceMap = new Map(); // 跳过表头,从第二行开始遍历 for (let i = 1; i < sourceData.length; i++) { const key = sourceData[i][0]; const values = sourceData[i].slice(1, 4); // 提取列2、3、4 sourceMap.set(key, values); } // 2. 批量处理Data表数据 const updatedData = data.map((row, index) => { // 跳过表头 if (index === 0) return row; // 检查第3列是否为空(索引从0开始,第3列对应索引2) if (!row[2]) { // 合并列1和列2的值(索引0和1) const mergedKey = row[0] + row[1]; // 从映射表获取匹配数据,没有则保留原空值 const matchedValues = sourceMap.get(mergedKey) || [row[2], row[3], row[4]]; // 更新当前行的列3、4、5(索引2、3、4) return [row[0], row[1], ...matchedValues]; } // 第3列不为空,直接返回原行 return row; }); // 3. 一次性写入所有更新后的数据 dataSheet.getDataRange().setValues(updatedData); }
优化说明
- 映射表
sourceMap提前存储所有Source数据,后续查找匹配值只需O(1)时间,避免了原代码中每次find()遍历整个数组的低效; - 原代码在循环内多次调用
setValues(),优化后只进行一次写入操作,大幅降低Spreadsheet API的调用开销; - 用
map()遍历Data表的每一行,一次性完成所有行的判断和更新逻辑,代码更简洁且效率更高; - 保留表头索引,避免
shift()导致的行索引混乱,减少出错概率。
内容的提问来源于stack exchange,提问作者Asking Bob
相关产品推荐
相关产品推荐

