sheet.appendRow() sporadically fails silently and results in an empty row - google-apps-script

Using google-apps-script from google-spreadsheet, I'm trying to fetch some hundreds of rows of data using fetchUrl and paste them into my sheet using sheet.appendRow.
Fetching data and splitting to arrays are OK. But appending them to a sheet row by row ocasionally fails without any errors and resulting in empty rows.
Here's quite simple example to recreate the problem.
function appendTest() {
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
sheet = SpreadsheetApp.setActiveSheet(sSheet.getSheets()[2]);
for (var i = 0; i <= 100; i++) {
sheet.appendRow([i, 1, 2, 3, 4, 5]);
}
}
About 5 or 10 rows, differs time to time, become empty.
Inserting Utilities.sleep() seems to be not working, either.
Does anybody have any idea to get rid of this situation?
Thank you in advance.

You could try to use batch writing to the sheet like this
function appendTest() {
var target = new Array()
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
sheet = SpreadsheetApp.setActiveSheet(sSheet.getSheets()[2]);
for (var i = 0; i <= 100; i++) {
target.push(['some data','in some columns','bla bla']);
}
sheet.getRange(sSheet.getLastRow()+1,1,target.length,target[0].length).setValues(target);
}
Note that if you think the sheet will be too 'short' you could append empty rows in the loop as well.
EDIT : following Michael pertinent answer, it would be a much better idea to add the necessary rows using sheet.insertRowsAfter(sheet.getMaxRows(), target.length)

Using appendRow inside a loop is flaky and not best practice. Please see:
https://productforums.google.com/forum/#!topic/docs/y0E9PB_VTJw
for a discussion of this issue.

Just a small comment:
I use appendRow in a loop, appending maybe up to 30 rows at a time. The script has been used daily for about a week now.
The interesting part is that during testing of the script, when I set it to append about 100 rows, then I too saw rows missing. But after a simple manual refresh of the destination spreadsheet, I saw no missing rows. So I took it that the rows were in fact appended, but that the bug? lies in their display.

Related

Google Scripts " e.changeType=='INSERT_ROW' " functionality - Reading when a new row is inserted

I'm trying to get my Google sheet to perform certain functions only when a new row is added. After some research I found the .changeType function which seems to work, but I'm having trouble then getting more functions to run based on that condition.
As you can see in my code, I'm using the .changeType function within an if statement.
The if statement appears to work, and it works up until the ui alert - which also works. However, none of the code below it does.
The weird part is that the same code works if I take it outside of the if function.
I'm having a lot of trouble debugging this as the Google Scripts debug tool thinks if(e.changeType=='INSERT_ROW') is an incorrect line of code, but I don't feel it is as it does actually run correctly in the Google sheet. I think this is something to do with the on change trigger functionality.
Perhaps it's the rest of my code that's incorrect, but I can't understand why it works when it's not within that if statement.
What I'm trying to do is to get the row that's just been added, by getting the active range of cells. Then, find the range of cells from immediately below that cell right to the last populated cell in the whole sheet. Those cells should then move with the .moveTo function.
Can anyone explain what's going wrong here? it's been impossible to debug so far!
Code is below:
function moveUpRows(e)
{
var sheet = SpreadsheetApp.getActiveSheet();
var ui = SpreadsheetApp.getUi();
if(e.changeType=='INSERT_ROW')
{
ui.alert("new row added!");
var range = sheet.getActiveRange();
var belowActiveRange = range.offset(1, 0);
var rangeMovingUp = sheet.getRange("L"+belowActiveRange.getRow()+":S"+sheet.getLastRow())
rangeMovingUp.moveTo(rangeMovingUp.offset(-1, 0));
}
}
If you insert a new row - this will lead to an undefined active range notation
In fact, Logger.log(range.getA1Notation()); will log #REF!
You can work around this limitation by defining:
...
var range = sheet.getActiveRange();
var row = range.getLastRow();
var belowActiveRange = sheet.getRange(row+1, 1, 1, sheet.getLastColumn());
...

Service error: Spreadsheets(line... with .setColumnFilterCriteria

This question is an extension from another.
Apply basic filter to multiple values in a spreadsheet column
I am experiencing an error, specifically Service error: Spreadsheets (line 8, file "Filter") with the following code:
function testFilter() {
var ss = SpreadsheetApp.getActive();
var monthlyDetailSht = ss.getSheetByName("Monthly_Detail");
var filterRange = monthlyDetailSht.getRange(2,12,359,1).getValues(); //Get L column values
var hidden = getHiddenValueArray2(filterRange,["Apple"]); //get values except Apple
var filterCriteria = SpreadsheetApp.newFilterCriteria().setHiddenValues(hidden).build();
var rang = monthlyDetailSht.getDataRange();
var filter = rang.getFilter() || rang.createFilter();// getFilter already available or create a new one
//remove filter and flush
if(monthlyDetailSht.getFilter() != null){monthlyDetailSht.getFilter().remove();}
SpreadsheetApp.flush();
filter.setColumnFilterCriteria(12, filterCriteria);
};
//flattens and strips column L values of all the values in the visible value array
function getHiddenValueArray2(colValueArr,visibleValueArr){
var flatArr = colValueArr.map(function(e){return e[0];}); //Flatten column L
visibleValueArr.forEach(function(e){ //For each value in visible array
var i = flatArr.indexOf(e.toString());
while (i != -1){ //if flatArray has the visible value
flatArr.splice(i,1); //splice(delete) it
i = flatArr.indexOf(e.toString());
}
});
return flatArr;
}
I have used a Logger.log(hidden) to capture the values returned by the function and it is a list of all of the other "fruits" repeated as many times as they are available in column L. I am using fruits as a substitute for the sensitive data.
So here goes my question. Why am I getting that error now when it was working perfectly fine for a couple of days? How can I correct this?
Attempted fixes:
I've tried to add rows to the end of my data. Did not fix.
I tried removing filter, flushing, setting filter. Did not fix. (updated code above with what I did to flush in case anyone else is interested.)
It's working now. A couple of things I want to note for people who stumble upon this with their google searches. First, the issue was in fact an error on Google's side. Using the same code I have above it now works. I did not change it.
Second, I was able to record the filtering through the macro recorder and that code worked when my original code did not. This may help people who are on a time crunch and can't wait for google to get their stuff together. I'm still not sure what specifically in my original code caused the error, but my guess is that it does not matter. I've dedicated a full day to researching this error and it seems sporadic with not a single culprit. My issue may not be the same as yours if it happens in the future.
Hope that helps!

Guild Roster Sheet - Setting value row-by-row in one column

I'm just starting to learn how to code with Google Apps Script. Barely.
I have a guild roster sheet (toon names in range B2:B and Realm (We also add prospects to this list) in range C2:C.
In Column D, I would like to set the value of each cell row-by-row to a function that uses the data in B and C (i.e. "=wowi(B2,C2)")
My intent is to just be able to add as many names as I want in Column B, and run the function manually to update the values for each in column D, and sleeps for a second between each (so as to not abuse the UrlFetch in the "=wowi(toonName, realmName)" function when the names column gets a little long)
Sadly, this is about as far as I got before I gave up:
function rosterUpdate() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destCell = sheet.getRange("D2");
for (x = 0, x //(less than or equal to)// sheet.getRange("B2:B").getNumRows(), x ++) {
if (x != 0 {
NOCLUEWHATTODO-IDKWHYIEVENTRY;
sleep(1000);
}
}
}
I know I need to use a for, but not quite sure on how to go about reading the value and modifying the D cell's value. I'm quite possibly overthinking this, as it's currently 5:30 in the morning and I'm running on about 2 hours of sleep, but would really appreciate the help.
Thanks in advance. Sorry if I'm bad. :[
Hopefully this answers your question in terms of getting and setting values (you should check the documentation: https://developers.google.com/apps-script/reference/spreadsheet/range) and how to sleep for one second between function calls, however it's pretty inefficient as it will iterate over the whole list each time.
Please be aware this is written off the top of my head very quickly so you may need to play with the i values to get it working properly, and there is probably a much, much cleaner way to do it!
var listLength = sheet.getLastRow() - 1;
for(var i = 0; i < listLength; i++){
var toonName = sheet.getRange("B" + (i+2)).getValue();
var realmName = sheet.getRange("C" + (i+2)).getValue();
sheet.getRange("D" + (i+2)).setValue(yourFunction(toonName, realmName));
Utilities.sleep(1000);
}

Google Drive - How to Autopopulate Google Spreadsheet with Concatenate functions when users submit data via webform?

I have a Google Drive Spreadsheet in which I'm trying to have a formula autopopulate all the way down the rows, as users submit data via a webform attached to the spreadsheet. The tricky part is that I use a CONCATENATE function already, to perform a concatenation of the data submitted on several columns into one single cell.
However, for the CONCATENATE function to work, I have to "apply" it to newly submitted rows.
Is there a way to automate the filling of this formula down the rows in the spreadsheet?
I've tried to place an ArrayFormula function to it, even setting the range (A1:A), but I couldn't find the proper syntax for it work, if it may like this.
The function goes:
=CONCATENATE(CHAR(10)&X14&V14&Y14&J14&" "&K14&" "&L14&M14&N14&O14&" "&P14&" "&Q14&R14&S14&CHAR(10)&T14&"."&CHAR(10)&U14&"."&CHAR(10)&W14&CHAR(10)&CHAR(10)&CHAR(10)&I14&CHAR(10)&Z14&" "&AA14&CHAR(10)&AB14&AC14&AD14&AE14&AF14&AG14&AH14&"."&CHAR(10)&AI14&AJ14)
Any suggestion will be greatly appreciated.
(Answered by the OP in a question edit. Transformed into a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
I've found a little script from the Script Gallery of Google Spreadsheet (AutoFormulas, by tuxincarnate[#]gmail[dot]com), which solves the trick! Just tested it with a dozen of submissions from the webform and it does what it promises, autopopulate with formulas down the columns where applied.
// Updates all rows except the last to have the same formulas as row 3 (allowing for a header and different row 2 formula)
// To activate this functionality, the last row in the column should have the value '(auto)'
function UpdateFormulas() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetCount = ss.getNumSheets();
for (var sheetIndex = 0; sheetIndex < sheetCount; ++sheetIndex) {
var sheet = ss.getSheets()[sheetIndex];
var rowCount = sheet.getMaxRows();
var columnCount = sheet.getMaxColumns();
if (rowCount < 5) continue;
for (var columnIndex = 1; columnIndex <= columnCount; ++columnIndex) {
if (sheet.getRange(rowCount, columnIndex).getValue() == '(auto)') {
var row3Range = sheet.getRange(3, columnIndex);
for (var rowIndex = 4; rowIndex < rowCount; ++rowIndex) {
if (sheet.getRange(rowIndex, columnIndex).isBlank()) {
row3Range.copyTo(sheet.getRange(rowIndex, columnIndex));
}
}
}
}
}
}
#David Tew wrote:
Whilst you have a solution already, you might like to consider for the future the following formula, which is what you were originally looking for (You don't need to use CONCATENATE since you are choosing the columns to join together with & symbol)
=arrayformula(CHAR(10)&X2:X&V2:V&Y2:Y&J2:J&" "&K2:K&" "&L2:L&M2:M&N2:N&O2:O&" "&P2:P&" "&Q2:Q&R2:R&S2:S&CHAR(10)&T2:T&"."&CHAR(10)&U2:U&"."&CHAR(10)&W2:W&CHAR(10)&CHAR‌​(10)&CHAR(10)&I2:I&CHAR(10)&Z2:Z&" "&AA2:AA&CHAR(10)&AB2:AB&AC2:AC&AD2:AD&AE2:AE&AF2:AF&AG2:AG&AH2:AH&"."&CHAR(10)&‌​AI2:AI&AJ2:AJ)
this should go in the second row
The OP wrote:
#DavidTew showed me a clear and easy way to have it solved. The ArrayFormula should go alone, telling it to put together the ranges for every column into every single row. Works like charm. Proper syntax is: =arrayformula(A2:A&B2:B&C2:C), in order to have contents from cells A2, B2 and C2 "concatenated" into the cell where the ArrayFormula is applied. The most important issue is that by using this function, it autpopulates all the way down the rows as users submit data via a webform attached to the spreadsheet.

Script to add rows, shift data and add characters for Google Docs Spreadsheet

I am running a project in Google Docs and I need help with a script. I need one that will perform the following tasks in a two column spreadsheet.
Insert a new blank row between every existing row of data.
Shift the data in the second column down one place so that they occupy the newly inserted blank row
Add curly brackets {} to the text in the data of the second column ie anytext --> {anytext}
Here is an example of how it is supposed to work:Sample GDocs spreadsheet
Your question was not far from the previous one that I just tried to answer so I though I could continue and suggest a working solution to this (easy) problem...
so there it is, not the most elegant but easy and working.
function insertBlank() {
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var last = lastRowinCol(sh,'A');
var colA = sh.getRange('A1:A'+last).getValues();
var colB = sh.getRange('B1:B'+last).getValues();
var nA = [];
var nB = [];
for(n=0;n<last;++n){
nA.push([colA[n]])
nA.push(['']);
nB.push([''])
nB.push(['{'+colB[n][0]+'}']);
}
Logger.log(nA.length+' = '+nA)
Logger.log(nB.length+' = '+nB)
sh.getRange(1,1,nA.length,1).setValues(nA);
sh.getRange(1,2,nB.length,1).setValues(nB);
}
function lastRowinCol(sh,col){
var coldata = sh.getRange(col+'1:'+col).getValues();
for(var c in coldata){if(coldata[c][0]==''){break}}
return c
}
That said, you should preferably ask more appropriate questions and show that you have tried before coming on sto make your shopping for free ;-)
EDIT : following a very pertinent comment from Mogsdad on this other post I suggest you replace the for-next loop in function lastRowinCol with his code that iterates backwards so that it handles empty cells in the column. Moreover his code has an interesting construction since the loop limits and the condition are in the same statement.
function lastRowinCol(sh,col){
var coldata = sh.getRange(col+'1:'+col).getValues();
for (var c=coldata.length; (c) && coldata[c-1][0]==''; c--) {}
return c
}