【问题标题】:A more efficient 'remove keywords' function更高效的“删除关键字”功能
【发布时间】:2018-01-25 20:48:07
【问题描述】:

之前,我询问过如何让“删除重复项”功能更高效。 Jordan Runner 和 Ed Nelson 在其他地方帮助了我,由此产生的代码对我的工作产生了巨大的积极影响。我也有一个“删除关键字”功能,该功能适用​​于少于 1,500 行的工作表。但是任何更大的东西都需要很长时间,有时会碰到“超过最大执行时间”。

如果有两个或三个关键字,即使我的代码也能正常工作。然而,我需要从有时超过 20,000 行(2 列)的工作表中删除大约 1,000 个关键字。有了这么大的工作表,我的代码不适合目的,我求助于将工作表分解成 2k 块。有人可以帮助使代码可用于大约 20,000 行的大工作表和需要删除的 1,000 个关键字集吗?

function removeKeywords() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var rows = sheet.getDataRange();
  var numRows = rows.getNumRows();
  var values = rows.getValues();
  var rowsDeleted = 0;

  for (var i = 0; i <= numRows - 1; i++) {
    var row = values[i];

    if (row[0].toLowerCase().indexOf("keyword1") > -1) {
      sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
      rowsDeleted++;
    }

    if (row[0].toLowerCase().indexOf("keyword2") > -1) {
      sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
      rowsDeleted++;
    }

    if (row[0].toLowerCase().indexOf("keyword3") > -1) {
      sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
      rowsDeleted++;
    }
  }
}

如其他地方所述,我是自学成才的。

【问题讨论】:

  • 这实际上是一个非常棘手的问题。您是否需要匹配单元格中任意位置的关键字,还是必须完全匹配(例如,hello world 是否匹配关键字 hello)?
  • 前者,即如果“democ”是目标关键词,那么所有包含“democracy”和“democratic”的单元格都需要被删除。这就是该功能目前的工作方式,我想保留这个有用的功能。
  • 是的,这是一件非常棘手的事情,因为复杂性很快就会增加。您的代码是一种合理的方法,时间复杂度 O*(*n*×*m*×*x),n 是行数,m 是关键字的数量,x 是在单元格中搜索关键字的复杂度(取决于indexOf 使用的搜索算法和关键字和搜索文本的长度)。
  • 好的,所以我应该坚持将大张纸分成小张的方法。更快的处理器能否克服 Google 电子表格的超时错误?
  • 如果您找不到其他解决方案,我会坚持使用该方法或更改函数以接受一系列单元格,然后在一张表中多次调用它(您可以自动选择下一个每次运行后的范围,这将使它不那么乏味)。更快的处理器对您没有帮助,因为 Apps 脚本运行在 Google 的服务器上,而不是您的计算机上。

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


【解决方案1】:

您还在寻找问题的解决方案吗?如果你正在这样做,这个示例脚本怎么样?修改点如下。

修改点:

  • deleteRow() 是通过使用反映搜索结果的数据覆盖来实现的,因为 deleteRow() 的成本很高。
  • 数组用于搜索关键字。

修改后的脚本:

function removeKeywords() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var range = sheet.getDataRange();
  var values = range.getValues();
  var formulas = range.getFormulas();
  var keywords = ["keyword1", "keyword2", "keyword3"]; // Please set keywords here.
  var resValues = [];
  for (var i in values) {
    if (keywords.filter(function(e){return ~values[i][0].indexOf(e)}).length == 0) {
      resValues.push(values[i]);
    }
  }
  var res = [];
  resValues.forEach(function(e1, i1) {
    var temp = [];
    e1.forEach(function(e2, i2) {
      temp.push(formulas[i1][i2] ? formulas[i1][i2] : e2);
    });
    res.push(temp);
  });
  sheet.clearContents();
  sheet.getRange(1, 1, res.length, res[0].length).setValues(res);
}

注意:

  • 当您使用此示例脚本时,请准备一个示例电子表格并运行它。或者请使用复制的电子表格。

如果这对你没有用,我很抱歉。

编辑:

function removeKeywords() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var range = sheet.getDataRange();
  var values = range.getValues();
  var formulas = range.getFormulas();
  var keywords = ["keyword1", "keyword2", "keyword3"]; // Please set keywords here.
  var resValues = [];
  var removedRows = [];
  for (var i in values) {
    if (keywords.filter(function(e){return ~values[i][0].indexOf(e)}).length == 0) {
      resValues.push(values[i]);
    } else {
      removedRows.push(values[i]);
    }
  }
  var res = [];
  resValues.forEach(function(e1, i1) {
    var temp = [];
    e1.forEach(function(e2, i2) {
      temp.push(formulas[i1][i2] ? formulas[i1][i2] : e2);
    });
    res.push(temp);
  });
  sheet.clearContents();
  sheet.getRange(1, 1, res.length, res[0].length).setValues(res);
  Logger.log(res)
  Logger.log(removedRows) // Removed rows
}

【讨论】:

  • 谢谢你,@Tanaike。我对具有 3,000 个条目和 5,000 个条目的列进行了测试,如下所示: 条目:3,000 原始功能时间:1m3s 最终计数:2757 修改功能时间:25s 最终计数:2842 条目:5,000 原始功能时间:2m27s 最终计数:4488 修改功能时间: 43s 最终计数: 4703
  • 抱歉,我不确定如何在 cmets 中正确格式化。无论如何,令人费解的是,原来的函数去掉了更多的关键字。知道为什么会这样吗?
  • 有没有办法判断哪个脚本在删除关键字方面更准确(除了手动检查)?我很困惑为什么我的原始脚本和修改后的脚本之间删除的关键字数量不同。
  • 例如,有没有办法生成包含已删除关键字的列表?
  • @Ed Dev 对于给您带来的不便,我深表歉意。首先,我可以确认你想要做什么吗?根据您的问题,我了解到您想删除关键字包含在 A 列中的行。A 列的每个值都由一些关键字搜索。如果该值包含关键字之一,则删除该行。如果我误解了你的问题,请告诉我。我想修改。我更新了我的答案。 “包含已删除关键字的列表”显示为removedRows。请确认。
【解决方案2】:

您需要创建异步函数,或者您可以使用 WebWorkers。在此处查看如何使用 WebWorker:Using web workers

现在,我可以给你一个没有 WebWorkers 的例子,那就是在 javascript 中使用 setTimeout() 函数。

function removeKeywords() {
    var sheet = SpreadsheetApp.getActiveSheet();
    var rows = sheet.getDataRange();
    var numRows = rows.getNumRows();
    var values = rows.getValues();
    var rowsDeleted = 0;


    var i = 0;

    function deleteRows() {
        setTimeout(function() {
            if (i <= numRows) {
                var row = values[i];

                if (row[0].toLowerCase().indexOf("keyword1") > -1) {
                    sheet.deleteRow((parseInt(i) + 1) - rowsDeleted);
                    rowsDeleted++;
                }

                if (row[0].toLowerCase().indexOf("keyword2") > -1) {
                    sheet.deleteRow((parseInt(i) + 1) - rowsDeleted);
                    rowsDeleted++;
                }

                if (row[0].toLowerCase().indexOf("keyword3") > -1) {
                    sheet.deleteRow((parseInt(i) + 1) - rowsDeleted);
                    rowsDeleted++;
                }

                i += 1;
                deleteRows();
            }
        }, 25);
    }

    deleteRows();
}

当然,这会比使用 Web Worker 慢,但至少不会阻塞您的浏览器。

更多信息setTimeout()here

【讨论】:

  • 返回以下错误: Missing ;声明之前。 (第 9 行,文件“代码副本”)
  • 请尝试使用var 而不是let
  • 谢谢,塞尔吉。但现在我得到:语法错误。 (第 11 行,文件“代码副本”)
  • 我已经编辑了我的原始答案。顺便问一下,你用的是什么浏览器?
  • 我使用的是 Firefox(不是 Opera,抱歉)。有问题吗?
猜你喜欢
  • 2018-10-18
  • 2018-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多