【问题标题】:Strange behaviour when trying to delete more then one row尝试删除多于一行时的奇怪行为
【发布时间】:2020-06-05 11:49:03
【问题描述】:

我在使用 Google Apps 脚本和 deleteRow()deleteRows() 方法这两个函数时遇到问题。

下面的代码使用字符串匹配测试列中是否存在字符串。如果字符串存在,那么我想做一些事情,在这种情况下,删除我活动工作表中的几行。

问题是它删除了第一行或一行,然后在我下面的代码中似乎没有删除下一行。

我已经能够验证,即使我从最后一行开始到第一行或从第一行到最后一行从工作表的末尾开始工作,也存在同样的问题。

它删除一行,但从不删除多于一行。在我的情况下,不会产生任何错误,只是使用任何一种方法都不会删除行。

    var data = ws.getRange("A2:U" + ws.getLastRow()).getValues();  

    var i = 0;
    var W = 23;
    var deleted = "DELETED"; 

    var sheetDataAsArr = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); // 2020Members 

    sheetDataAsArr.forEach(function(row,i)     {   

    // The i is a index 
       if ( row[W] = 'DELETED' ) {
          // DO SOMETHING HERE
            Logger.log('EMAIL DELETED ROW row is :' + row[B]);  // The email 
            ws.deleteRow(i+1);  // It deleted the record
            i--; // Decrement i because record was deleted 
       }
     i++; 
    });

这个问题很奇怪,我已经能够使用这两个函数进行复制。它不会删除下一行,但我可以看到下一行,因为我显示了正确的电子邮件地址,但没有通过循环第二次执行删除,但也没有产生任何错误消息,只是没有执行第二次删除一个,但删除第一行就好了。

它只允许我删除一行,最多删除一行

   var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("2020Members");

         The following above works fine now 

     var myValue = ws.getRange(i+1, W).getValue(); 

这是 W 列中工作表中的值,并且 if 现在也有效!

无论我是从最后一行到第一行但不是标题,它仍然只允许我删除一行并在下一行停止,所以如果我从第一行到最后一个或从最后一个到第一个遍历工作表不会让我只删除一行并停止。

在进行第二次删除之前我需要sleepflush 吗?

上面的第二个代码示例我什至尝试使用ws.deleteRows(i+1,1); 并发生同样的问题。

     The following above works fine now 

     var myValue = ws.getRange(i+1, W).getValue(); 

      This is the value in the sheet in the W column and the if works now also !

【问题讨论】:

  • 在进行第二次删除之前我是否需要睡觉或冲洗一些东西?
  • 你知道JS没有=比较运算符吧?你所做的是写了一个 总是 通过的if(true) 声明。接下来,i-- 什么都不做,因为forEach works 不是这样。此外,row[U] 将始终为true 或始终为false(要么不保持循环,要么与U 一起做某事)。一旦你处理了上述问题,问题就应该消失了
  • 对不起,我对应用程序脚本不熟悉,有点生疏!请解释 ?我确实知道 row[U] 在我的情况下是真还是假 iit 是在该列中选中的复选框!
  • GAS 是 JavaScript 之上的一个便利层,所以它下面是普通的旧 JS。请阅读foEachcomparison。并删除外部范围i - 它是pointless。好吧,你可以忽略关于row[U] 的声明——它并不总是真/假。
  • 是的,它有效,但它也一直有效(因为赋值返回分配的值,即DELETED,它是真实的,它总是评估为真)。至于您的问题-从您提供的代码来看,它应该删除ws表中从1到sheetDataAsArr.length的所有行。由于您可能在选中的复选框与在 col W 中设置的 DELETED 之间存在相关性,因此您会看到正确的结果(如果这就是您的意思 - 但您的嵌套 if 肯定不会进行任何比较)跨度>

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


【解决方案1】:

偏移问题

让我们想象一个假设的情况,您有 3 行,每行 2 个列。第二列包含选中的“DELETE”值。您的循环将执行 3 次迭代,因为值 length 等于 3

//第一次迭代: 位置 1,idx 0 --> row1 | col1 |删除 | - 匹配 第 2 行 | col1 |删除 | 第 3 行 | col1 | col2 | //第二次迭代: 第 2 行 | col1 |删除 | 位置 2,idx 1 --> row3 | col1 | col2 | -- 不匹配 //第三次迭代: 第 2 行 | col1 |删除 | 第 3 行 | col1 | col2 | 位置 3, idx 2 --> ---- | ------------- | ——越界

如您所见,每次forEach 移动到下一次迭代时,如果删除了一行,则使用您的算法的结果将“关闭”1。也就是说,如果我们使用实际进行比较的固定版本,否则第二次迭代也会删除一行。

跟踪已删除的行

更重要的是,从上面可以看出,在非顺序匹配中,结果偏移量将随着行数的增加而变得越来越大。您需要跟踪已删除的行并进行补偿。该示例应该可以让您很好地了解该怎么做:

function testDeleteRow() {

    const deletedRowNumbers = [];

    const ss = SpreadsheetApp.getActiveSpreadsheet();

    const testName = "test_deletion";

    const sh = ss.insertSheet();

    sh.setName(testName);

    const dimensions = [1, 1, 5, 2];

    const matrix3x2 = sh.getRange(...dimensions);

    matrix3x2.setValues([
        ["column1", "DELETE"],
        ["column1", "KEEP"],
        ["column1", "DELETE"],
        ["column1", "KEEP"],
        ["column1", "DELETE"]
    ]);

    const testRng = sh.getRange(...dimensions);

    const testVals = testRng.getValues();

    let offset = 0;

    testVals.forEach((rowOfValues, i) => {

        const [, toDelete] = rowOfValues;

        if (toDelete === "DELETE") {
            const offsetted = (i + 1 - offset) || 1;

            sh.deleteRow(offsetted);

            deletedRowNumbers.push( i + 1 );

            offset++;
        }

    });

    const ui = SpreadsheetApp.getUi();
    ui.prompt(`Deleted rows: ${deletedRowNumbers.join(", ")}`);

    ss.deleteSheet(sh);
}

备注

  1. 正如多次提到的,去掉i声明 并递增 - 它是 shadowed 通过 forEach 方法的回调 函数i 参数,因此增量实际上什么都不做。
  2. return false 是否会导致爆发 - 不,不会。请阅读forEach 上的documentation。有一个巨大的免责声明解释说,除非引发异常,否则您无法停止执行(forEach 字面意思是“对于每个元素都执行”,除非您想创建副作用,否则使用它是没有意义的数组中的每个元素)。
  3. 如果您需要从最后一行到第一行,不要从 0 开始,而是从值数组的最后一个索引开始。
  4. if(row[W] = 'DELETED') 与写 if(true) 相同。由于assignment 的工作方式,条件将评估为true - 它返回分配的值,因此if 语句实际上评估if("DELETED")
  5. 我需要睡觉或冲水吗 - 不,你不需要。刷新更改的唯一原因是让用户知道在您的脚本执行繁重的工作时正在发生某些事情,或者它是否依赖于非原子操作来完成,这不是您的情况。

【讨论】:

    【解决方案2】:

    脚本的一个“大问题”是

    i--; // Must auto decrement i
    

    内部

    sheetDataAsArr.forEach(function(row,i)     {
    
    }
    

    以上是因为Array.prototype.forEach() 在每次迭代中递增i。如果要进行反向迭代,请使用for

    相关

    【讨论】:

    • 所以不要使用 forEach 而是使用 for 循环?
    • Ruben - 将使用 for 也支持回调函数你能举个例子还是这里根本不需要回调?我确实想使用从最后一行到第一行的反向迭代!
    • 根据我的理解,我认为我更接近于为修改第二段代码获得正确的合成,但仍然对 For 而不是 forEach 感到困惑?
    • @whitejeep1953 我添加了一个相关问题的链接,其中包含使用for 进行反向迭代的示例代码。
    • 我能够通过不使用 forEach 而是使用 for 循环来解决删除行问题,但我不知道使用 forEach 发生了什么只能删除一行,所以我遇到了一个异常和脚本将使用回调函数结束,而不是使用 for 循环从下到上遍历行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-03
    • 2017-11-20
    相关资源
    最近更新 更多