【问题标题】:Long processing time likely due to getValue and cell inserts由于 getValue 和单元格插入,处理时间可能较长
【发布时间】:2016-05-19 06:27:21
【问题描述】:

我刚刚编写了我的第一个谷歌应用脚​​本,从 VBA 移植,它格式化了一列客户订单信息(感谢您的指导)。

说明:

代码通过它们的 - 前缀标识州代码,然后将以下名字与姓氏(如果存在)结合起来。然后它会在姓氏所在的位置写入“订单完成”。最后,如果订单之间没有间隔,它会插入一个必要的空白单元格(见下图)。

问题:

问题是处理时间。它不能处理更长的数据列。我被警告了

方法 Range.getValue 被脚本大量使用。

现有优化:

根据对this question 的回复,我尝试将尽可能多的变量保留在循环之外,并且还改进了我的 if 语句。 @MuhammadGelbana 建议只调用一次 Range.getValue 方法并使用它的值移动......但我不明白这将/可能如何工作。

代码:

function format() {

var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var lastRow = s.getRange("A:A").getLastRow();
var row, range1, cellValue, dash, offset1, offset2, offset3;

  //loop through all cells in column A
  for (row = 0; row < lastRow; row++) {
    range1 = s.getRange(row + 1, 1);

    //if cell substring is number, skip it
    //because substring cannot process numbers
    cellValue = range1.getValue();
    if (typeof cellValue === 'number') {continue;};
    dash = cellValue.substring(0, 1);

    offset1 = range1.offset(1, 0).getValue();
    offset2 = range1.offset(2, 0).getValue();
    offset3 = range1.offset(3, 0).getValue();

    //if -, then merge offset cells 1 and 2
    //and enter "Order complete" in offset cell 2.
    if (dash === "-") {
       range1.offset(1, 0).setValue(offset1 + " " + offset2);
       //Translate
       range1.offset(2, 0).setValue("Order complete");
     };

    //The real slow part...
    //if - and offset 3 is not blank, then INSERT CELL
    if (dash === "-" && offset3) {
       //select from three rows down to last
       //move selection one more row down (down 4 rows total)
       s.getRange(row + 1, 1, lastRow).offset(3, 0).moveTo(range1.offset(4, 0));
     };    
  };
}

格式更新:

有关使用字体或背景颜色格式化输出的指导,请查看此后续问题here。希望您能从这些专业人士给我的建议中受益:)

【问题讨论】:

  • @Rubén 如果您正在寻找其他答案,我可以问您吗?如果我对您的情况有误解,我深表歉意。
  • @Tanaike 目前的答案集中在 OP 的特定代码上,但有一些概念可以简要解释一下,以便为更广泛的受众提供服务,比如盯着 Google Apps 脚本中处理数组的人.这是因为最近有一个问题,OP 说他们不能使用有关错误消息的问题的答案。
  • 还有其他问题的核心答案完全相同:使用批处理操作
  • @Tanaike 别担心,感谢您的时间和问题。
  • @Rubén 例如,这份报告对您的情况有用吗? gist.github.com/tanaikech/d102c9600ba12a162c667287d2f20fe4

标签: javascript optimization multidimensional-array google-apps-script google-sheets


【解决方案1】:

使用像.getValue().moveTo() 这样的方法在执行时间上可能会非常昂贵。另一种方法是使用batch operation,您可以在其中获取所有列值,并在一次调用中写入工作表之前根据需要遍历数据整形。当您运行脚本时,您可能已经注意到以下警告:

脚本使用了一种被认为昂贵的方法。每个 调用会生成对远程服务器的耗时调用。那 可能对脚本的执行时间产生严重影响, 尤其是在大数据上。如果性能是脚本的问题, 您应该考虑使用另一种方法,例如Range.getValues()。

使用.getValues().setValues(),您的脚本可以重写为:

function format() {

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var s = ss.getActiveSheet();
  var lastRow = s.getLastRow(); // more efficient way to get last row
  var row;

  var data = s.getRange("A:A").getValues(); // gets a [][] of all values in the column
  var output = []; // we are going to build a [][] to output result

  //loop through all cells in column A
  for (row = 0; row < lastRow; row++) {
    var cellValue = data[row][0];
    var dash = false;
    if (typeof cellValue === 'string') {
      dash = cellValue.substring(0, 1); 
    } else { // if a number copy to our output array
      output.push([cellValue]); 
    }
    // if a dash  
    if (dash === "-") {
      var name = (data[(row+1)][0]+" "+data[(row+2)][0]).trim(); // build name
      output.push([cellValue]); // add row -state
      output.push([name]); // add row name 
      output.push(["Order complete"]); // row order complete
      output.push([""]); // add blank row
      row++; // jump an extra row to speed things up
    } 
  }
  s.clear(); // clear all existing data on sheet
  // if you need other data in sheet then could
  // s.deleteColumn(1);
  // s.insertColumns(1);

  // set the values we've made in our output [][] array
  s.getRange(1, 1, output.length).setValues(output);
}

用 20 行数据测试你的脚本,发现执行需要 4.415 秒,上面的代码在 0.019 秒内完成

【讨论】:

  • 作为最后一招,在使用这个 [ ] push 技术时是否可以调用函数?即 var rowPlus1 = (data[(row+1)[0]]).getA1Notation() -> ="Order in cell range " + rowPlus1 + " 完成!" -> "A5 单元格区域的订单已完成!"
  • getValues() 返回一个基本数组对象,因此无法访问 getA1Notation()。但是,您可以使用当前的 output.length() 来计算一行,例如rowPlus1 = "单元格区域 A 中的订单"+(output.length()+1)+" 完成!"; (未经测试,您可能需要将 +1 调整为 +2)
  • 我想不通,但我能够简单地使用 matrix.push(['The order ' + name + ' has been processed!']);谢谢大佬,结案!
【解决方案2】:

问题:

  • 在循环中使用 .getValue().setValue() 会导致处理时间增加。

文档摘录:

  • 尽量减少对服务的调用:

您可以在 Google Apps 脚本本身内完成的任何操作都比发出需要从 Google 服务器或外部服务器获取数据的调用(例如对电子表格、文档、站点、翻译、UrlFetch 等的请求)要快得多。

  • 前瞻缓存:

Google Apps 脚本已经有一些内置优化,例如使用前瞻缓存来检索脚本可能获取的内容并写入缓存来保存可能设置的内容。

  • 最小化读/写“数量”:

您可以编写脚本以最大限度地利用内置缓存,最大限度地减少读取和写入次数。

  • 避免交替读/写:

交替读取和写入命令很慢

  • 使用数组:

要加快脚本速度,用一个命令将所有数据读入一个数组,对数组中的数据执行任何操作,然后用一个命令写出数据。

慢速脚本示例:

/** 
 * Really Slow script example
 * Get values from A1:D2
 * Set values to A3:D4
 */

function slowScriptLikeVBA(){
  const ss = SpreadsheetApp.getActive();
  const sh = ss.getActiveSheet();
  //get A1:D2 and set it 2 rows down
  for(var row = 1; row <= 2; row++){
    for(var col = 1; col <= 4; col++){
      var sourceCellRange = sh.getRange(row, col, 1, 1);
      var targetCellRange = sh.getRange(row + 2, col, 1, 1);
      var sourceCellValue = sourceCellRange.getValue();//1 read call per loop
      targetCellRange.setValue(sourceCellValue);//1 write call per loop
    }
  }
}
  • 请注意,每个循环进行两次调用。有两个循环;在此示例中,为 2x4 数组的简单复制粘贴进行了 8 次读取调用和 8 次写入调用。
  • 此外,请注意,读取和写入调用交替进行,导致“预读”缓存无效。
  • 服务调用总数:16
  • 耗时:~5+ 秒

快速脚本示例:

/** 
 * Fast script example
 * Get values from A1:D2
 * Set values to A3:D4
 */

function fastScript(){
  const ss = SpreadsheetApp.getActive();
  const sh = ss.getActiveSheet();
  //get A1:D2 and set it 2 rows down
  var sourceRange = sh.getRange("A1:D2");
  var targetRange = sh.getRange("A3:D4");
  var sourceValues = sourceRange.getValues();//1 read call in total
  //modify `sourceValues` if needed
  //sourceValues looks like this two dimensional array:
  //[//outer array containing rows array
  // ["A1","B1","C1",D1], //row1(inner) array containing column element values
  // ["A2","B2","C2",D2],
  //]
  //@see https://stackoverflow.com/questions/63720612
  targetRange.setValues(sourceValues);//1 write call in total
}
  • 服务调用总数:2
  • 耗时:~0.2 秒

参考资料:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-02-19
    • 1970-01-01
    • 2011-08-22
    • 2019-05-01
    • 2018-01-04
    • 1970-01-01
    相关资源
    最近更新 更多