Omi*_*iod 32 performance google-sheets google-apps-script
我制作了一个脚本,每隔几个小时就会在Google Apps电子表格中添加一个新行.
这是我为找到第一个空行而做的功能:
function getFirstEmptyRow() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var cell = spr.getRange('a1');
var ct = 0;
while ( cell.offset(ct, 0).getValue() != "" ) {
ct++;
}
return (ct);
}
Run Code Online (Sandbox Code Playgroud)
它工作正常,但当达到约100行时,它变得非常慢,甚至十秒.我担心当达到数千行时,它会太慢,可能会超时或更糟.有没有更好的办法?
Don*_*kby 46
Google Apps脚本博客上发布了一篇关于优化电子表格操作的文章,该文章讨论了批量读取和写入操作,这些操作可以真正加快速度.我在100行的电子表格上尝试了你的代码,大约花了7秒钟.通过使用Range.getValues()
,批处理版本需要一秒钟.
function getFirstEmptyRow() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
}
return (ct);
}
Run Code Online (Sandbox Code Playgroud)
如果电子表格足够大,您可能需要以100或1000行的块来获取数据,而不是抓取整个列.
Mog*_*dad 35
这个问题现在有超过12K的观点 - 所以现在是时候进行更新了,因为New Sheets的性能特征与Serge进行初始测试时不同.
好消息:整体表现要好得多!
与第一次测试一样,只需读取一次工作表的数据,然后在阵列上操作,就可以获得巨大的性能优势.有趣的是,Don的原始功能比Serge测试的修改版本表现得更好.(看起来while
比for
这更快,这不符合逻辑.)
样本数据的平均执行时间仅为38毫秒,低于之前的168毫秒.
// Don's array approach - checks first column only
// With added stopping condition & correct result.
// From answer https://stackoverflow.com/a/9102463/1677912
function getFirstEmptyRowByColumnArray() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct] && values[ct][0] != "" ) {
ct++;
}
return (ct+1);
}
Run Code Online (Sandbox Code Playgroud)
以下是结果,总结了超过50次迭代的电子表格,包含100行x 3列(填充了Serge的测试功能).
函数名称与下面脚本中的代码匹配.
最初的问题是找到第一个空行.之前的任何脚本都没有真正实现.许多人只检查一列,这意味着他们可以给出误报结果.其他人只找到所有数据后面的第一行,这意味着错过了非连续数据中的空行.
这是一个符合规范的功能.它被包括在测试中,虽然比闪电般快速的单柱检查器慢,但却以相当可观的68毫秒,正确答案的50%溢价!
/**
* Mogsdad's "whole row" checker.
*/
function getFirstEmptyRowWholeRow() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var row = 0;
for (var row=0; row<values.length; row++) {
if (!values[row].join("")) break;
}
return (row+1);
}
Run Code Online (Sandbox Code Playgroud)
如果您想重复测试,或者将自己的函数添加到混合中作为比较,只需获取整个脚本并在电子表格中使用它.
/**
* Set up a menu option for ease of use.
*/
function onOpen() {
var menuEntries = [ {name: "Fill sheet", functionName: "fillSheet"},
{name: "test getFirstEmptyRow", functionName: "testTime"}
];
var sh = SpreadsheetApp.getActiveSpreadsheet();
sh.addMenu("run tests",menuEntries);
}
/**
* Test an array of functions, timing execution of each over multiple iterations.
* Produce stats from the collected data, and present in a "Results" sheet.
*/
function testTime() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getSheets()[0].activate();
var iterations = parseInt(Browser.inputBox("Enter # of iterations, min 2:")) || 2;
var functions = ["getFirstEmptyRowByOffset", "getFirstEmptyRowByColumnArray", "getFirstEmptyRowByCell","getFirstEmptyRowUsingArray", "getFirstEmptyRowWholeRow"]
var results = [["Iteration"].concat(functions)];
for (var i=1; i<=iterations; i++) {
var row = [i];
for (var fn=0; fn<functions.length; fn++) {
var starttime = new Date().getTime();
eval(functions[fn]+"()");
var endtime = new Date().getTime();
row.push(endtime-starttime);
}
results.push(row);
}
Browser.msgBox('Test complete - see Results sheet');
var resultSheet = SpreadsheetApp.getActive().getSheetByName("Results");
if (!resultSheet) {
resultSheet = SpreadsheetApp.getActive().insertSheet("Results");
}
else {
resultSheet.activate();
resultSheet.clearContents();
}
resultSheet.getRange(1, 1, results.length, results[0].length).setValues(results);
// Add statistical calculations
var row = results.length+1;
var rangeA1 = "B2:B"+results.length;
resultSheet.getRange(row, 1, 3, 1).setValues([["Avg"],["Stddev"],["Trimmed\nMean"]]);
var formulas = resultSheet.getRange(row, 2, 3, 1);
formulas.setFormulas(
[[ "=AVERAGE("+rangeA1+")" ],
[ "=STDEV("+rangeA1+")" ],
[ "=AVERAGEIFS("+rangeA1+","+rangeA1+',"<"&B$'+row+"+3*B$"+(row+1)+","+rangeA1+',">"&B$'+row+"-3*B$"+(row+1)+")" ]]);
formulas.setNumberFormat("##########.");
for (var col=3; col<=results[0].length;col++) {
formulas.copyTo(resultSheet.getRange(row, col))
}
// Format for readability
for (var col=1;col<=results[0].length;col++) {
resultSheet.autoResizeColumn(col)
}
}
// Omiod's original function. Checks first column only
// Modified to give correct result.
// question https://stackoverflow.com/questions/6882104
function getFirstEmptyRowByOffset() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var cell = spr.getRange('a1');
var ct = 0;
while ( cell.offset(ct, 0).getValue() != "" ) {
ct++;
}
return (ct+1);
}
// Don's array approach - checks first column only.
// With added stopping condition & correct result.
// From answer https://stackoverflow.com/a/9102463/1677912
function getFirstEmptyRowByColumnArray() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct] && values[ct][0] != "" ) {
ct++;
}
return (ct+1);
}
// Serge's getFirstEmptyRow, adapted from Omiod's, but
// using getCell instead of offset. Checks first column only.
// Modified to give correct result.
// From answer https://stackoverflow.com/a/18319032/1677912
function getFirstEmptyRowByCell() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var ran = spr.getRange('A:A');
var arr = [];
for (var i=1; i<=ran.getLastRow(); i++){
if(!ran.getCell(i,1).getValue()){
break;
}
}
return i;
}
// Serges's adaptation of Don's array answer. Checks first column only.
// Modified to give correct result.
// From answer https://stackoverflow.com/a/18319032/1677912
function getFirstEmptyRowUsingArray() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var data = ss.getDataRange().getValues();
for(var n=0; n<data.length ; n++){
if(data[n][0]==''){n++;break}
}
return n+1;
}
/**
* Mogsdad's "whole row" checker.
*/
function getFirstEmptyRowWholeRow() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var row = 0;
for (var row=0; row<values.length; row++) {
if (!values[row].join("")) break;
}
return (row+1);
}
function fillSheet(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
for(var r=1;r<1000;++r){
ss.appendRow(['filling values',r,'not important']);
}
}
// Function to test the value returned by each contender.
// Use fillSheet() first, then blank out random rows and
// compare results in debugger.
function compareResults() {
var a = getFirstEmptyRowByOffset(),
b = getFirstEmptyRowByColumnArray(),
c = getFirstEmptyRowByCell(),
d = getFirstEmptyRowUsingArray(),
e = getFirstEmptyRowWholeRow(),
f = getFirstEmptyRowWholeRow2();
debugger;
}
Run Code Online (Sandbox Code Playgroud)
Pet*_*ann 22
它已作为Sheet上的getLastRow方法存在.
var firstEmptyRow = SpreadsheetApp.getActiveSpreadsheet().getLastRow() + 1;
Run Code Online (Sandbox Code Playgroud)
参考https://developers.google.com/apps-script/class_sheet#getLastRow
看到这个有5k视图的老帖子我首先查看了"最佳答案"并且对其内容感到非常惊讶......这确实是一个非常缓慢的过程!当我看到Don Kirkby的答案时,我感觉更好,阵列方法确实更有效率!
但效率更高?
所以我在1000行的电子表格上写了这个小测试代码,结果如下:(不错!......不需要告诉哪一个是...)
这是我使用的代码:
function onOpen() {
var menuEntries = [ {name: "test method 1", functionName: "getFirstEmptyRow"},
{name: "test method 2 (array)", functionName: "getFirstEmptyRowUsingArray"}
];
var sh = SpreadsheetApp.getActiveSpreadsheet();
sh.addMenu("run tests",menuEntries);
}
function getFirstEmptyRow() {
var time = new Date().getTime();
var spr = SpreadsheetApp.getActiveSpreadsheet();
var ran = spr.getRange('A:A');
for (var i= ran.getLastRow(); i>0; i--){
if(ran.getCell(i,1).getValue()){
break;
}
}
Browser.msgBox('lastRow = '+Number(i+1)+' duration = '+Number(new Date().getTime()-time)+' mS');
}
function getFirstEmptyRowUsingArray() {
var time = new Date().getTime();
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var data = ss.getDataRange().getValues();
for(var n =data.length ; n<0 ; n--){
if(data[n][0]!=''){n++;break}
}
Browser.msgBox('lastRow = '+n+' duration = '+Number(new Date().getTime()-time)+' mS');
}
function fillSheet(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
for(var r=1;r<1000;++r){
ss.appendRow(['filling values',r,'not important']);
}
}
Run Code Online (Sandbox Code Playgroud)
并且测试电子表格自己尝试:-)
编辑:
根据Mogsdad的评论,我应该提到这些函数名称确实是一个糟糕的选择......它应该是一个getLastNonEmptyCellInColumnAWithPlentyOfSpaceBelow()
不是很优雅的东西(是吗?),但更准确和与它实际返回的内容一致.
评论:
无论如何,我的观点是显示两种方法的执行速度,它显然做到了(不是吗?;-)
我知道这是一个旧线程,这里有一些非常聪明的方法。
我使用脚本
var firstEmptyRow = SpreadsheetApp.getActiveSpreadsheet().getLastRow() + 1;
Run Code Online (Sandbox Code Playgroud)
如果我需要第一个完全空的行。
如果我需要列中的第一个空单元格,我会执行以下操作。
我的第二行是隐藏行,每个单元格都有公式
=COUNTA(A3:A)
Run Code Online (Sandbox Code Playgroud)
whereA
替换为列字母。
我的脚本只是读取这个值。与脚本方法相比,这更新得非常快。
有一次这不起作用,那就是当我允许空单元格拆分列时。我还不需要修复这个问题,我怀疑一个可能来自COUNTIF
,或一个组合函数或许多其他内置函数之一。
编辑: COUNTA
确实处理范围内的空白单元格,因此对“一次这不起作用”的担忧并不是真正的问题。(这可能是“新表格”的新行为。)
小智 5
为什么不使用appendRow?
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.appendRow(['this is in column A', 'column B']);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
66997 次 |
最近记录: |