I am getting the data range of a sheet and using Range.getNumRows() to get the number of rows in a Google Spreadsheet using Google Apps Script.
But when the sheet happens to be completely empty, Range.getNumRows() still returns 1 instead of 0. I am guessing this is because a range has to have at least 1 cell.
Is there another (simple) way to get the number of rows in a sheet without having this Problem?
I know I could loop through all cells in the sheet to check it is completely empty, but this doesn't seem very efficient.
I just stumbled across the answer on the app script documentation.
I am using sheet.getLastRow() now
Another option would be to get the sheet range and concatenate it to check if any data is found.
function isSheetEmpty(sheet) {
return sheet.getDataRange().getValues().join("") === "";
}
range.isBlank() work's well too.
Apps Script Range isBlank
Related
Sometimes this works and sometimes it doesn't. I have a Google Sheet with formulas containing ARRAYFORMULA and IMPORTRANGE formulas. The values are correctly displayed on the sheet. However, when I use a script to getValues or getDisplayValues, the script returns #N/A. This doesn't happen every time, either. Sometimes, if I refresh the sheet a few times, it works.
Here is an example formula that returns data in the sheet, but returns #N/A in the script:
=ARRAYFORMULA(if(A2:A="","",VLOOKUP(A2:A,{{arrayformula(left(Planner!D5:D13,find(" ",Planner!D5:D13)-1))},{Planner!W5:W13}},2,0)))
Apps Script instances run in Google's servers rather than in your browser, so this is probably an instance of the Google Sheets client in your browser showing one thing while the back-end spreadsheet engine reports another.
You may want to reset the formula cell and use flush(), perhaps together with Utilities.sleep() to avoid the #N/A issue, like this:
const formulaCell = SpreadsheetApp.getActive().getRange('Sheet1!A1');
const formula = formulaCell.getFormula();
formulaCell.setFormula(null);
SpreadsheetApp.flush();
formulaCell.setFormula(formula);
SpreadsheetApp.flush();
Utilities.sleep(1 * 1000);
const values = formulaCell.offset(0, 0, 10, 10).getValues();
console.log(JSON.stringify(values));
I'm using a piece of Google Script to copy from one Google Sheet to another Google Sheet, but also doing some column and data manipulation (so it's not just a straight copy).
The code was inspired by this question: Event trigger to move row to one of two other sheets based on values in 2 columns
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.range;
...
}
Unfortunately it seems that when you check Multiple checkboxes in one go by highlighting multiple cells and pressing the space bar, the OnEdit event object will only detect the first cell as being edited, and the other cells not being edited.
I tested this by doing:
Browser.msgBox(event.range.getNumRows())
No matter how many Checkboxes I edit in one go; it always returns 1
Is there a correct way to get the true range of Cells edited in one go?
This appears to be a bug!
I gave Google's Issue Tracker a look and it appears that this also happens when deleting multiple cells with checkboxes - only the range of the first cell is passed to e.range. Here's the report which details the same kind of behaviour:
Deleting a range of cells from a sheet passes only the range of Checkboxes in the deleted range to the onEdit(e) event object
It appears that this is an issue related to the way checkbox ranges are passed to the event object as well.
Google does seem to know about this issue but if it's causing problems you can file your own bug about it here as Diego mentioned above.
You can also hit the ☆ next to the issue number in the top left on the aforementioned pages which lets Google know more people are encountering this and so it is more likely to be seen to faster.
I am trying to learn Google Script. I want to write a script that uses a For Loop to save the value of each cell, clear the cell, then paste that saved value back into it. The data is in cells C6 - C17. So far, I am able to get my For Loop to work clearing each cell one by one with a 1sec pause in between but I cant figure out how to use setValue to copy the value back into the cell. If I change the last line to source.setValue("test") then my script works, clearing each cell then replacing it with "test".
Here is my code:
function forceRefresh() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Fidelity Daily");
for (var i =6 ; i<=17;i++)
{
var source = sheet.getRange(i,3);
var values = source.getValues();
source.clear();
Utilities.sleep(1000);
source.setValue(values);
}
}
Values retrieved by getValues() is 2 dimensional array. In your script, values is [["value"]]. So for your script in your question, when you want to import values to a cell using setValue(), it is required to modify as follows.
From :
source.setValue(values);
To :
source.setValue(values[0][0]);
Note :
If you want to retrieve one value from one cell, you can use getValue(). In this case, value retrieved by getValue() can be used by source.setValue(value);.
References :
getValue()
getValues()
setValue()
I don't know whether this is an answer for you. If I misunderstand your question, I'm sorry.
I was able to solve my issue by adding the command SpreadsheetApp.flush() after the source.clear() command.
The answer for your second question.
Utilities.sleep only runs in the beginning of each function only one time. I hope this helps.
And unfortunately there is no such command like delay(); or something, in google script.
The problem:
When using IMPORTRANGE to pull data from a different spreadsheet, cells in the origin spreadsheet that contain a formule containing VLOOKUP often (but not always) return #N/A (ERROR: Did not find value 'xxx' in VLOOKUP evaluation). In the origin sheet the formulas are calculated correctly and showing a value though. This does not always happen, sometimes it does pull in the proper value.
The intent:
To take data from different spreadsheets, combine them in a different spreadsheet and do some calculations on them. And then pull info from this calculation spreadsheet (or from multiple calculation spreadsheets) into a reporting spreadsheet for some further calculations and formatting.
The set-up:
There are several source data spreadsheets,say dataspreadsheet1, dataspreadsheet2 and dataspreadsheet3. A calculation spreadsheet (calcspreadsheet) is created that creates a copy of sheet1 in each of the data spreadsheets and names these sheets datasheet1, datasheet2 and datasheet3 respectively. The IMPORTRANGE statement used for this is created as follows: importrange(+VLOOKUP("dataspreadsheet1",filelist!A1:C1000,3,FALSE),"sheet1!a1:Z1000") where
filelist!A1:C1000 is a sheet in calcspreadsheet that contains Name, Type and Id in respective columns.
The values in each of these sheets datasheet1-3 are then used for calculations in another sheet, calcsheet1 in the calcspreadsheet. As the main goal of this is to add up daily values from the 3 dataspreadsheets, but those sourcesheets do not all have the same data on the same line a VLOOKUP is used again to make sure additions for a date use the line for the date in datasheet1-3 regardless of its row number. E.g. VLOOKUP($A11,'datasheet1'!$A:$P,4) + VLOOKUP($A11,'datasheet2'!$A:$P,4) + VLOOKUP($A11,'datasheet3'!$A:$P,4) where column A is the date column in all sheets.
This appears to work fine, although upon opening calcspreadsheet it can take a long time for it to go through an update, during which time lots of #N/A's are displayed. Eventually it comes right though.
The problem arise when a reportspreadsheet is created that in turn used an IMPORTRANGE call to pull the info from calcsheet1 in order to be able to work with it. This often, but not always, results in the problem states at the start. The IMPORTRANGE call in this reportspreadsheet is generated in a similar way as that in the calcspreadsheet: =importrange(+VLOOKUP(calc!B1,sheetcodes!A1:C3000,3,FALSE),"sheet1!a1:Z1000") where calc!B1 contains the name of the source spreadsheet (in this calc that would be 'calcspreadsheet' and sheetcodes!A1:C3000 again contains a list of sheets with Name, Type and Id in respective columns
A work-around I tried:
What I did notice that IMPORTRANGE works better on cells that do not contain VLOOKUP So I tried to copy the content of calcsheet to another sheet in calcspreadsheet, called exportsheet but having only the values in there, not formulas and then use IMPORTRANGE on this exportsheet. The copy script used is as follows:
function exportPrep() {
// Get the active spreadsheet and the active sheet
//var ss = SpreadsheetApp.getActiveSpreadsheet();
//var sheet = ss.getSheetByName("stream");
//sheet.activate();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("calcsheet");
var sourceDataRange = source.getDataRange();
var sourceSheetValues = sourceDataRange.getValues();
var sourceRows = sourceDataRange.getNumRows();
var sourceColumns = sourceDataRange.getNumColumns();
var destination = SpreadsheetApp.getActiveSpreadsheet();
SpreadsheetApp.setActiveSheet(destination.setActiveSheet("exportsheet"));
destination.getDataRange().offset(0, 0, sourceRows, sourceColumns).setValues(sourceSheetValues);
}
This seemed to work, but unfortunately the copy script used to copy the value of calcsheet into exportsheet now showed the same behaviour, it would sometimes work and sometimes give the #N/A and so leaves me with the same problem.
My questions:
I've read various posts with similar issues and responses that mentioned this function was temperamental or had a bug. Other stated it is not possible to use dynamic references in IMPORTRANGE. Given that it sometimes works and sometimes not I suspect the function itself might be correct but that there are sync or time-out issues in the set-up.
How can I set up the above functionality. Either with the use of IMPORTRANGE and VLOOKUP at all with some changes/additions, or built in a different way from the get-go.
So I've not done this with importrange but I when I have this issue with Vlookup I wrap the vlookup in an IF and test for the #N/A.
Try something like: =IF(ISNA(VLOOKUP(...)),"",VLOOKUP(...))
=IFERROR(VLOOKUP(B81,'KAM_Q3_OPPS STAGE_(H/I/L/M/N)'!$F$3:$G$111,2,False),)
This is the best way I found to do it and the last two values [ , ) ] make the new output, in this case it is nothing.
IF you wanted 0 put in then you would do [ ,0) ]
I have a script that I am running that writes a Google Drive file inventory to a log using the Logger.log() method.
How would I modify this to write the result to a spreadsheet instead?
Are you wanting to just set rows in a column to be file names?
If you want each list to be a new sheet try
var ssNew = SpreadsheetApp.create("File List");
I assume here you're iterating through the file list capturing one file name at a time. If you keep track of the last row you wrote to and increment that each time you should be able to call get range and get cell then set value something like this.
var currentRow = 0;
var range = ssNew.getRange(1, currentRow);
range.getCell(currentRow, 1).setValue(<file name>);
currentRow++;
With apps script sheets you're always getting a 'range' and never a cell in quite the way I think of it. Hope that helps