【问题标题】:Optimize iteration of 2-dimensional array优化二维数组的迭代
【发布时间】:2018-04-08 09:30:59
【问题描述】:

我正在尝试优化一个 Google Apps 脚本,它反向迭代电子表格中的行(从下到上)。如果过去 15 天,该脚本会检查行中的第一个单元格,并且还会检查第 12 列的字符串“是”,如果两个条件都为真,则该行从一张纸移动到另一张纸。

我遇到的问题是我超过了分配的时间(6 分钟),遍历了 2500 行的所有数据,并且预计会变得更大。

我确信类似的问题已经在这里回答过很多次了,但是鉴于我目前的编程技能水平,我无法应用在这里找到的解决方案。任何帮助将不胜感激。

这是表格的格式:

 1st column                 12th column
+-----------+-----/ /-----+-------------+
|  Date     |             | Refunded?   |
+-----------+-----/ /-----+-------------+
| 8/29/2017 |             |     YES     |
+-----------+-----/ /-----+-------------+
| 9/26/2017 |             |      NO     |
+-----------+-----/ /-----+-------------+

这是脚本:

function myFunction() {
  // get compare date (15 days from current date 
  var compareDate = new Date().getTime() - (15 * (1000 * 60 * 60 * 24));

  // get sheets in spreadsheet
  var target = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
  var arr = [];

  // get 1st sheet data
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  var values = sheet.getDataRange().getValues();
  var r = values.length;

  // logic
  while (r >= 1) {
    if (values[r] && values[r][0].getTime() < compareDate && values[r][12].toLowerCase() == "yes" ) {        
      arr.push(values[r])
      sheet.deleteRow(r + 1)
    }
    r--;
  }

  target.getRange(target.getLastRow() + 1, 1, arr.length, arr[0].length).setValues(arr.reverse())
}

如果我注释掉以下代码行“sheet.deleteRow(r + 1)”,脚本几乎立即完成,因此这似乎是减慢脚本执行速度的罪魁祸首。这个问题会有更有效的解决方案吗?

【问题讨论】:

  • 可以尝试两个临时数组,一个用于您要保留的行,另一个用于您要删除的行。然后只需像你一样将删除数组添加到第二页并使用keeper数组清除第一页和第一页上的setValues()。

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


【解决方案1】:

深入了解 Ruben 关于一次删除多行的想法。在实际开始比较之前对数据进行排序是个好主意,这样所有最旧的“是”值都位于底部。

为了检查它,我设置了一个包含 25000 条记录的工作表:

function fillSheet() {
  var yesNo = ['yes', 'no'];
  var array = [];
  for (var i = 0; i < 25000; i++) {
    array[i] = [];
    var rand = yesNo[Math.floor(Math.random() * yesNo.length)];
    array[i].push(new Date(2016, 1, i), i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9, i + 10, rand);
  }
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  sheet.getRange(2, 1, array.length, array[0].length).setValues(array);
}

然后我修改了您的函数,使其首先按是/否和日期对数据范围进行排序,然后进行检查(这次从顶部开始)。

function myFunction() {
  // get compare date (15 days from current date 
  var compareDate = new Date().getTime() - (15 * (1000 * 60 * 60 * 24));

  // get sheets in spreadsheet
  var target = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
  var arr = [];

  // get 1st sheet data
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  var lastRow = sheet.getLastRow();
  var lastCol = sheet.getLastColumn();
  //sorting the range without the header row firstly by yes/no values and then by dates to make sure the oldest yes value are at the bottom and only then getting the values
  sheet.getRange(2, 1, lastRow - 1, lastCol).sort([{column: 13, ascending: true}, {column: 1, ascending: false}]);  
  var values = sheet.getRange(2, 1, lastRow - 1, lastCol).getValues();
  var r = 0;
  var firstMatch = 0;

  // logic
  while (r < values.length) {
    if (values[r] && values[r][0].getTime() < compareDate && values[r][12].toLowerCase() == "yes" ) {
      //looking for the first match which will be used in deleting the rows
      if (firstMatch === 0) {
        firstMatch = r + 1; 
      }
      else if (firstMatch > 0 && firstMatch > r) {
        firstMatch = r + 1;
      } 
      arr.push(values[r])
    }
    r++;
  }
  if (firstMatch > 0) {
    target.getRange(target.getLastRow() + 1, 1, arr.length, arr[0].length).setValues(arr); //this will insert the array twice in my case anв i'm not sure why
    sheet.deleteRows(firstMatch, values.length - firstMatch + 2); //+2 is empirical to make sure all rows are removed; not really sure where it comes from
    sheet.getRange(2, 1, lastRow, lastCol).sort({column: 1, ascending: true});
  }
}

这样,运行大约需要 4 秒。

不过有几个小问题:

  • 它尝试将生成的数组两次插入到目标工作表中,尽管它显然不应该这样做。可能和我现在用的Vivaldi浏览器有关系,不知道。

  • 经验 firstMatch,values.length - firstMatch + 2 - 再次不确定为什么它应该 +2,但没有它不会删除最后 2 行。

希望对你有所帮助。

【讨论】:

  • 非常感谢您对此脚本的帮助。我正在用我的数据对其进行测试,一切似乎都在正常工作。我将继续测试,看看我是否看到异常。再次感谢您的帮助。
【解决方案2】:

与其通过迭代删除一行,不如在脚本末尾使用deleteRows(rowPosition, howMany) 一次性删除所有行。这行代码可能类似于以下代码:

sheet.deleteRows(2, values.length - 1);

【讨论】:

  • 嗨 Rubén,感谢您抽出宝贵时间回复我的问题。我正在尝试实现您的建议我只是想知道如果行不连续(即第 100、104、120、122 行等),我如何使用“deleteRow”方法?
  • @danslik:我认为将所需的行添加到新工作表中可能会更容易和更快。
猜你喜欢
  • 2015-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-28
  • 2015-04-04
  • 2014-05-30
  • 2020-06-09
  • 1970-01-01
相关资源
最近更新 更多