1

I have a system generated spreadsheet that enters blank cells when there is more than one item inside an order number. For example, if Order 1 had three items, I would have three rows completely filled but only one with date. The header is like this:

Order Number | User | Date | Item

I Attached an example image from a sample dataset:

Dataset image

I tried the following code which does the job greatly but I couldn't finish the task because the execution is timing out every time the script is run (it stops around row number 1,000). My spreadsheet has about 3,000 rows and growing slowly. I read some instructions about trying to make the code more efficient but for me it is already very efficient (at least comparing it to VBA). Any assistance is appreciated!

function myFunction() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var last = sheet.getLastRow();// get last row number
 
  for (i = 2; i < last; i++) { 
    var info = sheet.getRange(i, 3);
    Logger.log(info);
 
    if (info.isBlank()) {
      var data = sheet.getRange(i-1, 3).getValues(); //copy value from cell above with date
      sheet.getRange(i, 3).setValues(data); //paste value on empty cell
    } 
  }
}
Rubén
  • 34,714
  • 9
  • 70
  • 166
Haddocks
  • 13
  • 3

3 Answers3

2

I think that the reason of your issue is that getValues and setValues are used in the loop. By this, the process cost will be high. This has already been mentioned by Rubén's answer. So in order to reduce the cost, in your situation, how about the modification with the following flow?

  1. Retrieve values from the column "C" using getValues.
  2. Create an array for putting values to Spreadsheet.
  3. Put the created array to the Spreadsheet using setValues.

When above flow is reflected to your script, it becomes as follows.

Modified script:

function myFunction() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var last = sheet.getLastRow();// get last row number

  // I modified below script.
  var range = sheet.getRange("C2:C" + last);

  // 1. Retrieve values from the column "C".
  var values = range.getValues();

  // 2. Create an array for putting values to Spreadsheet.
  var temp = "";
  var v = values.map(([c]) => [c.toString() != "" ? temp = c : temp]);

  // 3. Put the created array to the Spreadsheet.
  range.setValues(v);
}

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
1

The problem is that getRange and other similar Spreadsheet Services (Class SpreadsheetApp) are "very" slow.

Instead of reading one range at each for iteration read the whole data range at once by using getDataRange() then getValues() then iterate over the Array that has all the data range values.

Related

Rubén
  • 34,714
  • 9
  • 70
  • 166
  • I thought that when a sample modified script for OP's situation is proposed, it might help to understand about the modification points. So I proposed it as one of several modifications. – Tanaike Jul 16 '20 at 23:08
  • 1
    Thanks for your comment @Tanaike. I'm hesitant to do that when there are already a lot of similar questions and the OP didn't included a brief description of their search/research efforts. Anyway, I'm glad that you posted an answer :) – Rubén Jul 16 '20 at 23:10
  • Thank you for replying. I can understand about it. I thought that when the user can get the direction for resolving the user's issue, it might lead to the education as the user. So I proposed about it. But of course, when I could find the the clear duplicated question for resolving the issue, I would like to propose to refer it. – Tanaike Jul 16 '20 at 23:15
  • 1
    Ruben, thank you for the material. I am going to read the articles you posted. Actually I am new to javascript and Google Sheets (usually do a bit of Python or VBA for small tasks) and I wasn't aware of the slow process behind `getRange` and other SpreadSheet Services. I tried to search for similar questions but found myself reading about making the code efficient which is a big step to a new user. – Haddocks Jul 17 '20 at 22:16
0

Try this:

function myFunction() {
  var ss=SpreadsheetApp.getActive();
  var sh=ss.getActiveSheet()
  var shsr=2;//data start row
  var vs=sh.getRange(shsr,3,sh.getLastRow()-shsr+1,1).getValues();
  vs.forEach(function(r,i){if(!r[0]){vs[i][0]=vs[i-1][0];}});
  sh.getRange(shsr,3,sh.getLastRow()-shsr+1,1).setValues(vs);
}
Cooper
  • 59,616
  • 6
  • 23
  • 54