我管理的Google表格列表有时超过10,000行。对于行数最多约5,000的工作表,下面提到的删除重复项功能可以正常工作。但是对于5,000以上的任何东西,我都会收到“超出最长执行时间”的错误。我将非常感谢有关如何使代码更高效的一些说明,即使对于具有10k +行的工作表,它也可以顺利运行。
function removeDuplicates() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var duplicate = false;
for(j in newData){
if(row.join() == newData[j].join()){
duplicate = true;
}
}
if(!duplicate){
newData.push(row);
}
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
答案 0 :(得分:5)
有一些事情会让你的代码变慢。让我们看看你的两个for
循环:
for (i in data) {
var row = data[i];
var duplicate = false;
for (j in newData){
if (row.join() == newData[j].join()) {
duplicate = true;
}
}
if (!duplicate) {
newData.push(row);
}
}
从表面上看,您正在做正确的事情:对于原始数据中的每一行,检查新数据是否已有匹配的行。如果没有,请将行添加到新数据中。但是,在此过程中,您需要做很多额外的工作。
例如,考虑一下这样一个事实,即data
中的一行在newData
中只有一行匹配。但是在你的内部for
循环中,在找到一个匹配后,它仍然继续检查newData
中的其余行。对此的解决方案是在break;
之后添加duplicate = true;
以停止迭代。
还要考虑对于任何给定的j
,newData[j].join()
的值始终相同。假设您在data
中有100行,并且没有重复(最坏的情况)。当你的功能完成时,你已经计算了newData[0].join()
99次,newData[1].join()
98次...总而言之,你已经完成了近5,000次计算以获得相同的99次值。对此的解决方案是memoization,您可以存储计算结果,以避免以后再次进行相同的计算。
即使您进行了这两项更改,您的代码time complexity仍然是O(n²)。如果你有100行数据,在最坏的情况下,内循环将运行4,950次。对于10,000行,这个数字约为5000万。
然而,我们可以做到这一点是 O ( n )时间,如果我们摆脱内部循环并重新形成外部循环,如下所示:
var seen = {};
for (var i in data) {
var row = data[i];
var key = row.join();
if (key in seen) {
continue;
}
seen[key] = true;
newData.push(row);
}
此处,我们不会在每次迭代中检查newData
的每一行中是否匹配row
的行,而是将我们所见过的每一行存储到对象{{1 }}。然后在每次迭代中,我们只需要检查seen
是否具有匹配seen
的密钥,我们可以在几乎恒定的时间内执行的操作,或者 O ( 1 < / em>的)。 1
作为一个完整的功能,这就是它的样子:
row
您会看到,而不是使用function removeDuplicates_() {
const startTime = new Date();
const sheet = SpreadsheetApp.getActiveSheet();
const data = sheet.getDataRange().getValues();
const numRows = data.length;
const newData = [];
const seen = {};
for (var i = 0, row, key; i < numRows && (row = data[i]); i++) {
key = JSON.stringify(row);
if (key in seen) {
continue;
}
seen[key] = true;
newData.push(row);
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
// Show summary
const secs = (new Date() - startTime) / 1000;
SpreadsheetApp.getActiveSpreadsheet().toast(
Utilities.formatString('Processed %d rows in %.2f seconds (%.1f rows/sec); %d deleted',
numRows, secs, numRows / secs, numRows - newData.length),
'Remove duplicates', -1);
}
function onOpen() {
SpreadsheetApp.getActive().addMenu('Scripts', [
{ name: 'Remove duplicates', functionName: 'removeDuplicates_' }
]);
}
此代码使用row.join()
,因为JSON.stringify(row)
是脆弱的(例如row.join()
)。 ['a,b', 'c'].join() == ['a', 'b,c'].join()
不是免费的,但对我们来说这是一个很好的妥协。
在我的测试中,这会处理一个简单的电子表格,其中包含50,000行和2列,时间超过8秒,或者每秒约6,000行。