【问题标题】:Google App Script: TypeError: Cannot read property 'length' of undefined (line *, file "Code")Google App 脚本:TypeError:无法读取未定义的属性“长度”(行 *,文件“代码”)
【发布时间】:2022-01-15 12:55:45
【问题描述】:

目标

防止脚本将导致错误的空白(空)行从源复制到目标。


错误详情:

Google App 脚本:TypeError:无法读取未定义的属性“长度”(行 *,文件“代码”)

此错误,指的是以下行: dstSheet.getRange(dstLastRow + 1, 1, values.length, values[0].length).setValues(values);


完整代码

我正在使用HERE 中的以下代码,该代码已稍作修改。无论有没有修改,都会发生错误。代码的逻辑是:如果destination sheet不包含ID,import ID,以及从source到destination的具体列。

function importNewEmployeeIds() {
  const srcSheetName = 'Source';
  const dstSheetName = 'Destination';
  const srcSheet = SpreadsheetApp.openById("SpreadsheetID1").getSheetByName(srcSheetName);

  const dstSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheetName);

  // Retrieve values from source sheet.
  var headerRowNumber = 1;
  const srcValues = srcSheet.getDataRange().offset(headerRowNumber, 0, srcSheet.getLastRow() - headerRowNumber).getValues();

  // Retrieve values from destination sheet and create an object for searching the ID.
  const dstLastRow = dstSheet.getLastRow();
  const dstObj = dstLastRow == 0 ? {} : dstSheet.getRange("A1:A" + dstLastRow).getValues().reduce((o, [a]) => (o[a] = true, o), {});

  // Create an array for putting to the destination sheet.
  const values = srcValues.filter(r => !dstObj[r[1]]).map(r => [r[1], r[19], "", r[18]]);

  // Put the values to the destination sheet.
  dstSheet.getRange(dstLastRow + 1, 1, values.length, values[0].length).setValues(values);
}

疑难解答

[来源] 之前脚本运行。

|  A   |B|  C   |  D   |
|200001| |Email2|Email1|
|200001| |Email2|Email1|
|200006| |Email4|Email3|
|200007| |Email6|Email5|
|      | |      |      |

[Destination] 之前脚本运行。

|  A   |B|  C   |  D   |
|200001| |Email2|Email1|

[Destination] 之后脚本运行。

|  A   |B|  C   |  D   |
|200001| |Email2|Email1|
|200006| |Email4|Email3|
|200007| |Email6|Email5|
|      | |      |      | <-- Notice the blank row. I don't want the blank row.

脚本运行后,会将行数据从源复制到目标。问题是,脚本还会将空白(空)行复制到源工作表中。

[目的地] 预期结果 - 没有空白行

|  A   |B|  C   |  D   |
|200001| |Email2|Email1|
|200006| |Email4|Email3|
|200007| |Email6|Email5|

我能想到的菜鸟解决方案:

选项 A) 在脚本的开头,我可以首先从目标中删除所有空行。这效率较低,并不能解决问题的根源,但至少它解决了我的问题。

function removeEmptyRows(){
  var sh = SpreadsheetApp.getActiveSheet();
  var maxRows = sh.getMaxRows(); 
  var lastRow = sh.getLastRow();
  if (maxRows-lastRow != 0){
    sh.deleteRows(lastRow+1, maxRows-lastRow);
    }
}

找到代码:HERE


选项 B) 更新原始代码,使其不会复制空白行。

[之前]

  const srcValues = srcSheet.getDataRange().offset(headerRowNumber, 0, srcSheet.getLastRow() - headerRowNumber).getValues();

[之后]

  const srcValuesAndBlankRows = srcSheet.getDataRange().offset(headerRowNumber, 0, srcSheet.getLastRow() - headerRowNumber).getValues();
  const srcValues = srcValuesAndBlankRows.filter(String); //Removes Blank Rows

找到代码:HERE

【问题讨论】:

  • 虽然我不确定我是否能正确理解你的问题,但我提出了一个修改后的脚本作为答案。你能确认一下吗?如果我误解了你的问题,我很抱歉。如果我提出的脚本没有用,您能否提供您期望的示例输入和输出情况作为图像?通过这个,我想修改脚本。
  • @Tanaike 我已经修改了我的问题,使其更短,更容易理解。我添加了表格来展示脚本运行前、脚本运行后以及脚本运行后的预期结果。我希望这更容易理解:)

标签: javascript google-apps-script google-sheets


【解决方案1】:

以你的情况为例,下面的修改怎么样?我修改了脚本中的两部分。

修改后的脚本:

function Q70718274_importNewEmployeeIds() {
  const srcSheetName = 'Source';
  const dstSheetName = 'Destination';
  const srcSheet = SpreadsheetApp.openById("SpreadsheetID1").getSheetByName(srcSheetName);

  const dstSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheetName);

  // Retrieve values from source sheet.
  var headerRowNumber = 1;
  const srcValues = srcSheet.getDataRange().offset(headerRowNumber, 0, srcSheet.getLastRow() - headerRowNumber).getValues().filter(r => r.join("") != ""); // <--- Modified

  // Retrieve values from destination sheet and create an object for searching the ID.
  const dstLastRow = dstSheet.getLastRow();
  const dstObj = dstLastRow == 0 ? {} : dstSheet.getRange("A1:A" + dstLastRow).getValues().reduce((o, [a]) => (o[a] = true, o), {});

  // Create an array for putting to the destination sheet.
  const values = srcValues.filter(r => !dstObj[r[1]]).map(r => [r[1], r[19], "", r[18]]);

  // Put the values to the destination sheet.
  if (values.length > 0) { // <--- Added
    dstSheet.getRange(dstLastRow + 1, 1, values.length, values[0].length).setValues(values);
  }
}
  • const srcValues = srcSheet.getDataRange().offset(headerRowNumber, 0, srcSheet.getLastRow() - headerRowNumber).getValues().filter(r =&gt; r.join("") != ""); 删除了空行。
  • if (values.length &gt; 0) {},当values没有值时,不会发生错误。

补充:

根据您的以下附加信息,

[来源] 之前脚本运行。

|  A   |B|  C   |  D   |
|200001| |Email2|Email1|
|200001| |Email2|Email1|
|200006| |Email4|Email3|
|200007| |Email6|Email5|
|      | |      |      |

[Destination] 之前脚本运行。

|  A   |B|  C   |  D   |
|200001| |Email2|Email1|

[Destination] 之后脚本运行。

|  A   |B|  C   |  D   |
|200001| |Email2|Email1|
|200006| |Email4|Email3|
|200007| |Email6|Email5|
|      | |      |      | <-- Notice the blank row. I don't want the blank row.

脚本运行后,会将行数据从源复制到目标。问题是,脚本还会将空白(空)行复制到源工作表中。

[目的地] 预期结果 - 没有空白行

|  A   |B|  C   |  D   |
|200001| |Email2|Email1|
|200006| |Email4|Email3|
|200007| |Email6|Email5|

我了解到您希望从前 2 个表中实现底部表。从您提供的脚本中,我认为您想使用const values = srcValues.filter(r =&gt; !dstObj[r[1]]).map(r =&gt; [r[1], r[19], "", r[18]]);。但是,从你补充的情况来看,我明白我的理解是不正确的。对于您的其他示例情况,以下脚本如何?

示例脚本:

function importNewEmployeeIds2() {
  const srcSheetName = 'Source';
  const dstSheetName = 'Destination';
  const srcSheet = SpreadsheetApp.openById("SpreadsheetID1").getSheetByName(srcSheetName);

  const dstSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dstSheetName);

  // Retrieve values from source sheet.
  var headerRowNumber = 1;
  const srcValues = srcSheet.getDataRange().offset(headerRowNumber, 0, srcSheet.getLastRow() - headerRowNumber).getValues().filter(r => r.join("") != ""); // <--- Modified

  // Retrieve values from destination sheet and create an object for searching the ID.
  const dstLastRow = dstSheet.getLastRow();
  const dstObj = dstLastRow == 0 ? {} : dstSheet.getRange("A1:A" + dstLastRow).getValues().reduce((o, [a]) => (o[a] = true, o), {});

  // Create an array for putting to the destination sheet.
  // const values = srcValues.filter(r => !dstObj[r[1]]).map(r => [r[1], r[19], "", r[18]]);
  const values = srcValues.filter(r => !dstObj[r[0]]).map(r => [r[0], "", r[2], r[3]]); // <--- Modified

  // Put the values to the destination sheet.
  if (values.length > 0) { // <--- Added
    dstSheet.getRange(dstLastRow + 1, 1, values.length, values[0].length).setValues(values);
  }
}

【讨论】:

  • 感谢您的解决方案。 if (values.length &gt; 0) {} 修复了错误,这很好。但是空行仍在使用新代码 const srcValues = srcSheet.getDataRange().offset(headerRowNumber, 0, srcSheet.getLastRow() - headerRowNumber).getValues().filter(r =&gt; r.join("") != ""); 从源复制到目标
  • @JamesReed68 感谢您的回复。我带来的不便表示歉意。从您的其他示例情况来看,我没有注意到您不想使用const values = srcValues.filter(r =&gt; !dstObj[r[1]]).map(r =&gt; [r[1], r[19], "", r[18]]);。这是因为我的英语水平不好。我对此深表歉意。对于您的其他示例情况,我又添加了一个脚本。你能确认一下吗?如果这不是您问题的直接解决方案,您能否提供示例电子表格以正确复制您的问题,包括脚本?借此,我想确认一下。
  • 非常感谢 Tanaike 的辛勤工作。这是我的错,不是你的英语。问题不在于您的脚本。当我创建一个示例电子表格来复制问题时,您的脚本运行良好(数据下没有空白行)。在我真正的电子表格上,我仍然遇到问题(数据下有很多空白行)。所以我需要调查它有什么问题。不幸的是,我们无法一起了解真实表格有什么问题,因为由于隐私,我无法共享真实的电子表格。但是,如果我找到解决方案,我会分享它以帮助其他人。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-12
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 2020-07-20
相关资源
最近更新 更多