Using this macro I have as issue #NAME?. Can you help me to solve?
function macro()
{
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++)
{
var rowData = data[i];
var parameter1 = rowData[10];
var parameter2 = rowData[11];
if (parameter1 == "OK" && parameter2 != "OK")
{
sheet.getRange("M" +(i+1)).activate();
sheet.getCurrentCell().setFormula('=DATA.VALORE(A' +(i+1)+ ')+7*(6-I' +(i+1)+ ')');
}
}
}
When using Google Sheets macros (Google Apps Script) to write formulas, functions should be written in English.
This was learned from experience. There isn't official documentation about this.
Related
Google Sheets formula generated by a GAS script throws an error, even though it's correct
Error:
DATA.VALORE is not a valid function name, thus it shows #NAME. Use valid function names.
If you were meaning to use DATEVALUE, then use it instead.
That would yield into a number like 44928, use TO_DATE or format that column to DATE instead of automatic to convert that back to date.
Code:
sheet.getCurrentCell().setFormula('=TO_DATE(DATEVALUE(A' + (i + 1) + ')+7*(6-I' + (i + 1) + '))');
Output:
Reference:
Formula parse errors
DATEVALUE
TO_DATE
Related
I'm trying to find cells that have an error, using a Range in script. The Range consists of a single column AB of cells using Sparkline() getting data from GoogleFinance(), which quite often return Error Google Finance internal error., and display #N/A. Errors are showing:
However, the function is not returning anything when I try to getValues:
function portfolioRefreshSparklines(){
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Portfolio');
const msg = 'Refreshing...';
const err = '#N/A';
var range = sheet.getRange('Portfolio_Sparklines');
var col = range.getColumn();
var rowStart = range.getRow();
Logger.log('col: ' + col + '; rowRange: ' + rowStart);
var data = range.getValues();
for ( i=0; i<data.length; i++ ) {
// this is NOT returning the `#N/A` error (`Google Finance internal error.`)
var rv = data[i][0];
Logger.log('i: ' + i + ' rv: '+ rv)
// If an error is found, set the cell's formula to the msg, then back to the original formula.
// Think I have to reference the cell directly to do the setFormula() switch, not within the data array?
if ( rv.includes(err) ){
var row = rowStart + i;
var cell = sheet.getRange(row, col);
Logger.log('cell: ' + cell.getA1Notation() );
rv = cell.getFormula();
cell.setFormula(msg);
cell.setFormula(rv);
}
}
SpreadsheetApp.flush();
}
I've searched through the Range Class, tried to use function getDisplayValues(), but haven't found anything that returns a cell error.
Any suggestions pls?
From the question
However, the function is not returning anything when I try to getValues:
Google Finance is blocked in Google Apps Script. See Reading the values of cells that summarize Google Finance data results via Apps Script
P.S.
It doesn't make sense to include SpreadsheetApp.flush() as the last function statement. It should be used when you need to force that the changes made are applied before the function ends because you will be reading something that was changed by the script to be used in later part of it.
The Best Practices discourages the use of Google Apps Script classes (in this case var cell = sheet.getRange(row, col);) in loops because they are slow.
I have a model in Google Sheets that is set up with one column per day. It contains both actuals and forecasts, and every day I need to roll forward formulas to replace forecasts with actuals. I can't roll forward the whole column, only a segment of it (there are reference numbers above and below that shouldn't be changed).
I have tried to write a script to do this for me every day, but I don't know how to make getRange reference a dynamic range. This is my attempt:
function rollColumn() {
var ss2 = SpreadsheetApp.openById('<ID redacted>');
ss2.getRange("=index(Model!$7:$7,,match(today()-2,Model!$4:$4,0)):index(Model!$168:$168,,match(today()-2,Model!$4:$4,0))").copyTo(ss2.getRange("=index(Model!$7:$7,,match(today()-1,Model!$4:$4,0)):index(Model!$168:$168,,match(today()-1,Model!$4:$4,0))"))
};
The INDEX formulas work insofar as they reference the relevant ranges (I have tested them in the spreadsheet). But clearly getRange doesn't accept formulas as an input. It also seems that Google Sheets doesn't allow for a named range to be created with formulas (which is how I would solve this in Excel).
Can someone help me recreate this functionality with GAS?
This is the closest existing question I've found on Stack Overflow, but I haven't been able to make it work:
Google Apps Script performing Index & Match function between two separate Google Sheets
Thank you!
You should add {contentsOnly:false} parameter to your code. something like this:
TemplateSheet.getRange("S2:T2").copyTo(DestSheet.getRange("S2:T"+LRow2+""), {contentsOnly:false});
Getting a date from column's title, then pasting formulas to the row to the right:
// note: we assume that sheet is disposed as in the following document: https://docs.google.com/spreadsheets/d/1BU2rhAZGOLYgzgSAdEz4fJkxEcPRpwl_TZ1SR5F0y08/edit?ts=5a32fcc5#gid=0
function find_3formulas() {
var sheet = SpreadsheetApp.getActiveSheet(),
leftTitle, // this variable will stay unused because we do not need a vertical index
topTitle = todayMinus_xDays(2),
topTitlesRange = sheet.getRange("G3:T3"),
leftTitlesRange = sheet.getRange("A4:A8"); // this range will stay unused.
var coor = findCoordinates(leftTitlesRange, leftTitle, topTitlesRange, topTitle);
if (coor.row == null || coor.column == null) {
sheet.getRange("M12:M14").setFormula('="NULL: please check logs"');
return;
}
var rowAxis = 4 + coor.row;
var colAxis = 8 + coor.column;
var fromRange = sheet.getRange(rowAxis, colAxis, 3, 1);
var toRange = sheet.getRange(rowAxis, colAxis + 1, 3, 1);
Logger.log(fromRange.getA1Notation())
Logger.log(toRange.getA1Notation());
var threeFormulas = fromRange.getFormulas();
toRange.setFormulas(threeFormulas)
}
// unused in current script!
function findCoordinates(leftTitlesRange, leftTitle, topTitlesRange, topTitle) {
var formattedDate,
row = 0,
column = 0;
if (leftTitle) {
row = findRow(leftTitlesRange, leftTitle);
}
if (topTitle) {
column = findColumn(topTitlesRange, topTitle);
}
var array = {row:row, column:column}
return array;
}
// unused in current script!
function findRow(range, valueToSearch) {
var colRows = range.getValues();
for (i = 0; i < colRows.length; i++) {
if (valueToSearch == colRows[i][0]) {return i;}
}
// however, if found nothing:
Logger.log("the value " + valueToSearch + " could not be found in row titles");
return null;
}
// assumes that column titles are dates, therefore of type object.
function findColumn(range, valueToSearch) {
var colTitles = range.getValues();
for (i = 0; i < colTitles[0].length; i++) {
if (typeof colTitles[0][i] == "object") {
formattedDate = Utilities.formatDate(colTitles[0][i], "GMT", "yyyy-MM-dd")
};
if (valueToSearch === formattedDate) {return i;}
}
// however, if found nothing:
Logger.log("today's date, " + valueToSearch + ", could not be found in column titles");
return null;
}
// substracts 2 days from today, then returns the result in string format.
function todayMinus_xDays(x) {
var d = new Date();
d = new Date(d - x * 24 * 60 * 60 * 1000);
d = Utilities.formatDate(d, "GMT", "yyyy-MM-dd");
return d;
}
I'm using google sheets and have never run a "cron job" here.
To give credit where it's due, I found this awesome code to help me access the speed insights api here: https://statsravingmad.com/measure/page-speed-insights/
function speed(url,device,filter_third_party_resources,http_secure) {
url = url || 'www.statsravingmad.com';
strategy = 'desktop' || device;
filter_third_party_resources = 'true' || filter_third_party_resources;
http_secure = 'false' || http_secure ;
switch (http_secure) {
case 'false':
http_protocol = 'http://';
break;
case 'true':
http_protocol = 'https://';
break;
}
var key = 'api-key';
var api = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=' + http_protocol + url
+ '&filter_third_party_resources=' + filter_third_party_resources +
'&strategy=' + strategy + '&key=' + key;
var response = UrlFetchApp.fetch(api, {muteHttpExceptions: true });
var result = JSON.parse(response.getContentText());
score = result.ruleGroups.SPEED.score;
return(score);
}
So I have this code in a function that is triggered every hour for my particular test sites in my google sheet.
But, the data is only filling one cell per site, the cell that the formula is assigned to.
When using google sheets, how can I modify this in order to have it fill a new cell in a column every hour? Do I modify this code, do I have to set up another function, or is there an option to fill the cells down a column?
This function can be modified to write, say, to column C of Sheet1. Here is how it would end, instead of return(score) (there is no need to return anything if the value is written to the spreadsheet directly; the function would not be invoked from the spreadsheet, but from a trigger).
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var values = sheet.getRange("C:C").getValues(); // using column C
var lastRow = 1;
for (var i = 0; i < values.length; i++) {
if (values[i][0] != "") {
lastRow = i + 1;
}
}
sheet.getRange(lastRow + 1, 3).setValue(score); // column = 3 because C
Here the loop finds the last row in column C that has data, and the values of score is placed under it.
I have created a script to send emails to a specific people with Birthday Reminder. This use to work till day before yesterday. I don't know why am I getting this error that Cannot find function getMonth, Can anyone tell where is the mistake
function emailAlert() {
// Short circuit if email notice is set to "No". This causes an error and the script stops.
if (turnOnEmailNotice.toLowerCase() == "no")
{
Logger.log("The Email Notification is NOT turned ON. System will Exit.");
exit
}
//Get the total number of filled row in the sheet.
var currentRowAT = 2;
var currentCellValueAT = "start";
while (currentCellValueAT != ""){
if (currentCellValueAT = birthdaysSheet.getRange("G" + currentRowAT).getValue() != ""){
currentRowAT = currentRowAT +1;
}
}
var birthdaysSheetLastRow = currentRowAT - 1;
// Get today's Date (with Month, Date and Year)
var today = new Date();
var todayMth = today.getMonth()+1;
var todayDate = today.getDate();
var todayYear = today.getFullYear();
// Check sheet cell for match to alertDate, k is the current row number being checked. Starting at 2 as the row #1 is for the headers
for (k=2; k < birthdaysSheetLastRow + 1; k++)
{
var targetBday = new Date();
targetBday = birthdaysSheet.getRange("P" + k).getValue();
// If Birthday is not speicified, continue with the next row
if (targetBday == ""){continue};
var unadjTargetBday = new Date();
var unadjTargetBdayMth = targetBday.getMonth()+1;
var unadjTargetBdayDate = targetBday.getDate();
var unadjTargetBdayYear = targetBday.getFullYear();
var unadjTargetBday = targetBday;
targetBday.setDate(targetBday.getDate()-daysInAdvance); // Calculating how many days in advance you want to trigger the notification. This is set in Settings Tab.
var targetBdayMth = targetBday.getMonth()+1;
var targetBdayDate = targetBday.getDate();
if (targetBdayMth + " " + targetBdayDate == todayMth + " " + todayDate)
{
var targetBirthDateYearsOld = (today.getYear() - unadjTargetBday.getYear())-1900;
prepareAndSendEmail(k, targetBirthDateYearsOld);
}
}
}
getValue will return the type string/date/number depending on the cell type in the spreadsheet (see menu Format -> Number). To be sure, just always convert to Date Type. This is the right way to convert it:
var targetBday = new Date(birthdaysSheet.getRange("P" + k).getValue());
Short answer
Check that the cell value is a valid date for Google Sheet.
Explanation
From https://developers.google.com/apps-script/reference/spreadsheet/range#getvalue (emphasis mine)
getValue()
Returns the value of the top-left cell in the range. The value may be
of type Number, Boolean, Date, or String depending on the value of the
cell. Empty cells will return an empty string.
To avoid this kind of problems, you may include some sort of data validation. You could use build-in features like conditional formatting, data validation, or something like a on edit script together with try / catch or a redundant validation in your emailAlert script.
I used the following function to iterate CONCATENATE in the spreadsheets. However, it shows the following error
Missing; before statement. (line 11, file "Code").
function iterate()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var startRow = 2;
var lastRow = sheet.getLastRow();
for (var i = startRow; i <= lastRow; i++)
{
var result(i) = =CONCATENATE("http://cdn.staticmb.com/mbphoto/property/original_images/",A(i), "/ ",E(i), "/" ,C(i),"/",D(i)) ;
var range = sheet.getRange("F2,F15");
range.setValues(result(i));
}
}
Besides several javascript syntax errors like those pointed out in comments, the biggest issue is that you are trying to use "=CONCATENATE" in javascript. Its not a javascript function, none of the sheet cell formulas are, those can only be used inside sheet cells. You need to code the concatenation using javascript functions or operators like var a= b + "x" + d. That cleared out it should be easy to code it or google for it. You are also using result(i) where apparently you want to use an array. Look up the correct syntax for creating and adding elements to an array in javascript.