Google Apps脚本在电子表格中双重删除行

Google Apps Script double deleting rows in spreadsheet

本文关键字:删除行 电子表格 Apps 脚本 Google      更新时间:2023-09-26
function onEdit() {
var openRequests = SpreadsheetApp.getActive().getSheetByName('Open Requests');
var lastRowOpen = openRequests.getLastRow();
var closedRequests = SpreadsheetApp.getActive().getSheetByName('Closed Requests');
var lastRowClose = closedRequests.getLastRow();
var closed = openRequests.getRange(2,8,lastRowOpen,1).getValues();
for (var i = 0; i < lastRowOpen; i++)
{
    if (closed[i][0].toString() == 'Yes')
    {
        var line = i+2;
        if (closedRequests.getLastRow() == 1)
        {
            openRequests.getRange(line,1,1,9).copyTo(closedRequests.getRange(2,1,1,9));
            closedRequests.getRange(2,9,1,1).setValue(new Date());
            openRequests.deleteRow(line);
        }
        else
        {
            openRequests.getRange(line,1,1,9).copyTo(closedRequests.getRange(lastRowClose+1,1,1,9));
            closedRequests.getRange(lastRowClose+1,9,1,1).setValue(new Date());
            openRequests.deleteRow(line);
        }
    }
}

}

我设置了一个触发器来运行onEdit。它所做的是检查一列Closed看它是否写着YesClosed列有一个数据验证下拉菜单,值为Yes

所以当我点击下拉菜单并选择Yes时,它应该将整行复制到另一个名为Closed Requests的工作表中,然后从名为Open Requests的电子表格中删除该行。

我遇到的问题是,大约50%的时间,它删除了我选择Yes的行,但它也删除了它下面的行(大约50%的时间,当这种情况发生时,只有一些时候第二个删除的行出现在Closed Requests中,其他时间整个行只是永远消失,除非我撤消)。

据我所知,deleteRow()函数删除整行并将其下方的所有行上移一行以填充空白。所以要删除的那一行下面的那一行被移到了同一行,也被删除了。我不知道为什么这个函数被调用了两次。

我试着添加一些延迟,但它似乎不工作。

function onEdit(e) {
  var eRange = e.source.getActiveRange();
  var openRequests = SpreadsheetApp.getActive().getSheetByName('Open Requests');
  var closedRequests = SpreadsheetApp.getActive().getSheetByName('Closed Requests');
  var nextRowClose = (closedRequests.getLastRow()?closedRequests.getLastRow()+1:2);
  if(eRange.getSheet().getName()=="Open Requests" && eRange.getColumn()==8 && eRange.getValue()=="Yes") {
    openRequests.getRange(eRange.getRow(), 1, 1, 9)
      .copyTo(closedRequests.getRange(nextRowClose, 1));
    closedRequests.getRange(nextRowClose, 9).setValue(new Date());
    openRequests.deleteRow(eRange.getRow());
  }
}

可以尝试像我提到的那样向后迭代。在删除后抛出SpreadsheetApp.flush()也可能有所帮助。

@Jack,我有一个和你类似的用例。我的代码是BryanP讨论的反向代码。我的代码或多或少在这里:"批量删除状态= 'Done'的任务项"。这是因为我在一批中删除它们,我使用向后方法,即删除具有较高行号的行不会干扰具有较低行号的任何行的行号。

但是你不是在批处理模式下删除行,所以也许向后不应该有什么区别(也许除非两个用户同时使用表和删除?)

所以我想试试你的代码。我将您的代码插入到我的电子表格上已经存在的onedit()函数中(该函数用于在一段时间不活动后将行涂成红色,并在实际参加任务后放入时间戳)。

然后为了测试,我使用了一个已经有50行/任务的电子表格副本。我手动填写一行中所需的单元格,并通过下拉菜单从单元格中选择Done(我更改了代码以期望"Done"而不是"Yes")。我重复了20行。

结果:你的代码成功了,正如你所期望的那样,每一次都成功了。没有双重删除,总是复制数据。它为我工作,没有引入延迟和SpreadsheetApp.flush()。

恐怕我没有可靠的建议。顺便提一下,我提到了电子表格本身没有正确刷新的已知错误,因此没有显示删除的行;这可以通过在出现此错误时手动刷新电子表格来检查。(然而,这个错误的迹象似乎在逻辑上不符合你的报告关于两个连续的行重复复制。)

线程锁定?听起来像是线程锁问题。试一试:

function onEdit() {
 // ****** add lock code
 var lock = LockService.getPublicLock();
 var hasMutex = lock.tryLock(100);
 if(hasMutex==false) {
   return;
 }
 // *** end
 var openRequests = SpreadsheetApp.getActive().getSheetByName('Open Requests');
 var lastRowOpen = openRequests.getLastRow();
 var closedRequests = SpreadsheetApp.getActive().getSheetByName('Closed Requests');
 var lastRowClose = closedRequests.getLastRow();
 var closed = openRequests.getRange(2,8,lastRowOpen,1).getValues();
 for (var i = 0; i < lastRowOpen; i++)
 {
    if (closed[i][0].toString() == 'Yes')
    {
       var line = i+2;
       if (closedRequests.getLastRow() == 1)
       {
          openRequests.getRange(line,1,1,9).copyTo(closedRequests.getRange(2,1,1,9));
          closedRequests.getRange(2,9,1,1).setValue(new Date());
          openRequests.deleteRow(line);
       }
        else
       {
   openRequests.getRange(line,1,1,9).copyTo(closedRequests.getRange(lastRowClose+1,1,1,9));
        closedRequests.getRange(lastRowClose+1,9,1,1).setValue(new Date());
        openRequests.deleteRow(line);
        }
      }
  }

 // ****** add lock code
  lock.releaseLock();    
 // *** end
} 

问题:

1)当时有多少人在使用电子表格。