非表格格式数据集如何使用GAS实现合并与转置
Google Sheets 数据合并转置实现方案
你当前的源数据结构如下:

核心需求是把每行对应单个物料、每列对应单个日期数值的宽表,转置为每行对应「单物料+单日期」单条记录的长表结构,通用转置方案没有适配你表中的空列偏移,所以会出现适配失败的问题。
实现步骤
1. 先对齐数据偏移关系
先把现有数据的索引对应关系理清楚,避免转置错位:
dates数组第一个元素是空字符串,对应源表C列的空表头,后面的元素从D列开始依次对应各日期- 每行物料数据的结构固定:第1位是序号、第2位是物料名、第3位是空值、从第4位开始依次是各日期对应的数值,和
dates数组中非空的日期元素一一对应
2. 核心转置逻辑
不需要复杂的通用转置函数,直接用双层遍历生成结果即可,适配你当前的表结构:
- 外层遍历所有物料行,解构提取固定字段(序号、物料名),跳过空列
- 内层遍历每个物料对应的数值列表,按索引匹配对应日期,拼接为单条长表记录
- 按需过滤空日期、空值的无效行
3. 可直接运行的完整代码
补全你现有savedata函数的逻辑即可:
function savedata() { const ss = SpreadsheetApp.getActiveSpreadsheet(); const itemsSheet = ss.getSheetByName('Items'); const lastCol = itemsSheet.getLastColumn(); // 读取并格式化日期表头 let dates = itemsSheet.getRange(2, 3, 1, lastCol - 2).getValues().flat(); dates = dates.map(e => e !='' ? Utilities.formatDate(new Date(e), Session.getTimeZone(), "MM/dd/yyyy") : '') // 读取物料源数据 const dataRng = itemsSheet.getRange(4, 1, itemsSheet.getLastRow() - 3, lastCol); const data = dataRng.getValues(); // 转置生成结果 const output = []; // 写入结果表头,可按需调整字段 output.push(["序号", "物料名称", "日期", "指标值"]); data.forEach(itemRow => { // 解构提取字段,跳过第三列的空值 const [serial, itemName, , ...valueList] = itemRow; valueList.forEach((val, idx) => { // 日期索引偏移1位,匹配dates数组的位置 const currentDate = dates[idx + 1]; // 不需要过滤空值的话可以删掉下面这行 if(!currentDate || val === '') return; output.push([serial, itemName, currentDate, val]); }) }) // 结果写入新工作表 const resultSheet = ss.getSheetByName('转置结果') || ss.insertSheet('转置结果'); resultSheet.clearContents(); resultSheet.getRange(1, 1, output.length, output[0].length).setValues(output); }
注意事项
- 代码里把原来硬编码的物料行数
3改成了动态读取行号的写法,后续新增物料不需要改代码,会自动识别读取 - 如果需要保留空值记录,直接删除过滤空值的判断逻辑即可
- 如果后续表结构调整(比如空列位置变化、日期起始列变化),只需要调整解构的跳过位置、日期索引的偏移量即可,不需要修改核心遍历逻辑
内容的提问来源于stack exchange,提问作者onit
相关产品推荐
相关产品推荐

