我制作了一个示例脚本,您应该可以在https://docs.google.com/spreadsheets/d/11u0xkdtPlQsnVppCnPYM0CHuCTPLdmN8PcFlaW08lNw/edit#gid=0看到
您必须制作一个副本才能实际使用它。
但是......复制不会给你我设置的触发器。如果您编辑脚本,请转到菜单Reources --> Current project's triggers并使自己成为函数checkForChanges()的基于时间的触发器。出于测试目的,我将其设置为“每分钟”。
在您的专栏中,我添加了四个新专栏:
- concat - 只是您需要监控其更改的所有行值的串联
- 当前哈希- 由我添加到您的脚本中的一个简单函数生成
- 上次编辑- 发送电子邮件后,脚本会为该单元格提供当前哈希值
- 已更改- 比较上次编辑和当前哈希,如果它们不同,则说 **true*。
所以......函数checkForChanges()定期在ChangeDetector范围内运行,寻找true。如果它什么也没找到,它会立即退出。
每次确实发现更改时,它都会收集该行的数据并通过电子邮件发送给它。(实际上,为了简单起见,我只是记录它。)
关键技巧是这对线:
lastEdits[row_][0] = currentHashCodes[row_][0];
ss_.getRangeByName("LastEdit").setValues(lastEdits);
请注意,如果您尽可能使用命名范围,您的代码会变得多么清晰。
如果示例在未来的某一天丢失,下面是代码:
/* Called from cells in column "Current Hash" */
function strHash(valCell) {
var hash = 0;
if (valCell.length == 0) return hash;
for (i = 0; i < valCell.length; i++) {
char = valCell.charCodeAt(i);
hash = ((hash<<5)-hash)+char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
/* Called periodically from a timed trigger. */
function checkForChanges() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var changeDetector = ss.getRangeByName("ChangeDetector").getValues();
for (row in changeDetector) {
if (row > 0) {
changed = changeDetector[row][0];
if (changed) notify(ss, row);
}
}
}
/* Called by checkForChanges(). */
function notify(ss_, row_) {
initializeRangeArrays(ss_);
status = statii[row_][0];
project = projects[row_][0];
task = tasks[row_][0];
customer = customers[row_][0];
executor = executors[row_][0];
deadline = deadlines[row_][0];
sendto = recipients[row_][0];
var mysubject = status + ' | ' + project + ': ' + task + ' - ' + ss_.getName() + ' update';
var mybody = '\nStatus: ' + status
+ '\n\nproject: ' + project
+ '\ncustomer: ' + customer
+ '\ntask: ' + task
+ '\nexecutor: ' + executor
+ '\nDeadline: ' + deadline
+ '\n\n' + ss_.getName()
+ ': \n' + ss_.getUrl();
Logger.log("to: " + sendto);
Logger.log("subject: " + mysubject);
Logger.log("body: " + mybody);
Logger.log("");
lastEdits[row_][0] = currentHashCodes[row_][0];
ss_.getRangeByName("LastEdit").setValues(lastEdits);
};
var recipients = null;
var projects = null;
var customers = null;
var tasks = null;
var deadlines = null;
var executors = null;
var statii = null;
var lastEdits = null;
var currentHashCodes = null;
var rangeArraysInitialized = false;
/* Called by notify(). */
function initializeRangeArrays(ss_) {
if ( ! rangeArraysInitialized ) {
recipients = ss_.getRangeByName("Recipient").getValues();
projects = ss_.getRangeByName("Project").getValues();
customers = ss_.getRangeByName("Customer").getValues();
tasks = ss_.getRangeByName("Task").getValues();
deadlines = ss_.getRangeByName("Date").getValues();
statii = ss_.getRangeByName("Status").getValues();
executors = ss_.getRangeByName("Executor").getValues();
lastEdits = ss_.getRangeByName("LastEdit").getValues();
currentHashCodes = ss_.getRangeByName("CurrentHash").getValues();
rangeArraysInitialized = false;
}
}
2014 年 9 月 22 日更新:
我在演示电子表格中做了一些更改。请看一看。
在我添加的脚本中。. .
function strArrayHash(range) {
var ret = new Array();
var str = "";
for (item in range) {
str = range[item].toString();
if (str.length > 0) {
ret[item] = strHash(str);
Utilities.sleep(50); // play with this to reduce "internal execution error"s
} else {
ret[item] = "";
}
};
return ret;
}
在“P”列中,我替换了 . . .
=if( M2 = "", "", strHash(R2)))
=if( M3 = "", "", strHash(R3)))
:
:
=if( M22 = "", "", strHash(R22)))
. . . 和 。. .
=ARRAYFORMULA(if( M2:M514 = "", "", strArrayHash(R2:R514)))
. . . 仅在单元格“P2”中。我还删除了“P3:P22”范围内所有单元格的内容
我将总行数增加到 1026
一旦值出现在 P 列中,我使用 [Paste Special] » [Paste values only] 将单元格 P2:P1026 复制到 M2:M1026 中。
在我的 i7 笔记本电脑中,重新计算并检测到第 500 行的变化需要 30 秒。
如果我尝试执行所有 1024 行,则会收到内部执行错误。
可能您需要使“if”子句复杂化,以便仅在真正需要它的行上进行哈希计算。
更新 :: 2017/02/24 没有人对此感兴趣,所以我停止了运行脚本的触发器。