In my Google Apps Script addon for Sheets, I have a custom function that will lookup prices. For example, a call like:
=LOOKUP_PRICE("apple")
would return the price for an apple. However, I've noticed that many users are not quoting the item name, instead just typing the following (which Sheets permits):
=LOOKUP_PRICE(apple)
When I try to retrieve the string value of the parameter with toString() all I get is the string "#NAME?".
Is there a way to handle this scenario and ideally retrieve the value of the parameter when not quoted?
Alternatively, is there a good way to detect this case and show the appropriate error message? All I've come up with so far is the following, but it feels dirty:
param.toString().toLowerCase() === "#name?"
Since these functions are written in JavaScript, as soon as you try to pass a string that isn't inside double quotes it will throw an error.
From the Custom Functions Documentation:
Google Sheets stores data in different formats depending on the nature of the data. When these values are used in custom functions, Apps Script treats them as the appropriate data type in JavaScript.
This means the functions use the normal JavaScript Data Structures i.e. string, number etc.
Your only real option here is to educate your users on how to use the function properly.
The easiest thing you could do to advise your users is give the function a JsDoc #customfunction tag, which allows you to customize the help text for the function itself. This helps your users with the syntax of your custom function.
Here's a brief example that you could modify to suit your requirement:
/**
* Searches for the price of the item specified.
*
* #param {"Apple"} item_to_find The item to find, e.g. "Apple", "Pear".
* #return Price of the item specified.
* #customfunction
*/
function LOOKUP_PRICE(item_to_find) {
if (item_to_find === "Apple") {
return "$4.99";
} else {
return "Not Found!";
}
}
When we start to call the custom function, we get this:
References:
Custom Functions in Google Sheets
JavaScript Data Structures
Related
Suppose I want to define a "useful" function that takes a THREE.Vector2 as well as some scaler values as inputs. What's the best syntax for defining the function if I want other people to easily understand the types of the parameters that need to be passed into the function? Sample (that doesn't work):
export function clipToBox(v: THREE.Vector2, boxWidth, boxHeight) {
const clippedVector = new THREE.Vector2
// Do some clever clipping math...
return clippedVector
}
Example of what we want users of our function to see when editing
From a previous question linked here ( Previous Question ) I learned about Sheets.SpreadSheets.get calling a JSON of sheet data that would allow me to get the backgroundcolors of a sheet within my project. Id previously been doing this with var BackgroundColors = ActiveWeekSheet.getDataRange().getBackgrounds(); but was told that the JSON method would be a faster read/write method. They directed me to do some reading on Javascript objects but after that I'm still confused.
I've got the following code. TestArray = Sheets.Spreadsheets.get("1irmcO8yMxYwkcLaxZd1cN8XsTIhpzI98If_Cxgp1vF8"); which seems to call a JSON with sheet specific data. A logger statement of TestArray returns this: testArrayObject: {"properties":{"gridProperties":{"rowCount":1000,"columnCount":26},"sheetType":"GRID","index":0,"sheetId":0,"title":"Awesome"}}
Community members previously suggested I could then find the background colors at: sheets[].data[].rowData[].values[].cellData.effectiveFormat.backgroundColor
I've highlighted one of the cells yellow but when reviewing the above JSON i can't seem to find anything that references color. There definitely isn't any multileveling of the JSON to refer to sheets->data->rowData->values->celldata.effectiveFormat.backgroundColor.
What am I missing here? Do I need to format things someway? Am I not calling the right JSON to start with?
Thanks!
As written in the documentation,
By default, data within grids will not be returned. You can include grid data one of two ways:
Specify a field mask listing your desired fields using the fields URL parameter in HTTP
Sheets.Spreadsheets.get(spreadsheetId, {
ranges:"Sheet1!A1:A5",
fields:"sheets(data(rowData(values(effectiveFormat.backgroundColor))))"
})
Set the includeGridData URL parameter to true. If a field mask is set, the includeGridData parameter is ignored
Sheets.Spreadsheets.get(spreadsheetId, {
ranges:"Sheet1!A1:A5",
includeGridData: true
})
Field mask documentation:
In a nutshell,
multiple different fields are comma separated, and
subfields are dot-separated.
For convenience, multiple subfields from the same type can be listed within parentheses.
You may test the API here
There are optional parameters in the spreadsheets.get method that will give you that data, but you need to explicitly include them:
ranges – The ranges to retrieve from the spreadsheet.
includeGridData – The cell data within specified range.
This specifies a range of just one cell (A1 in Sheet1), but you can specify a larger range and navigate through the array if you need to.
var TestArray = Sheets.Spreadsheets.get(SS_ID, {ranges: "Sheet1!A1", includeGridData: true});
Really important that you keep in mind this returns a Color object with RGBA
values that range from 0-1, but elsewhere apps script uses hex color or the conventional 0-255 RGB values.
On the Data Studio authentication documentation (https://developers.google.com/datastudio/connector/auth), there are two functions that need to be defined which I am confused by.
In the setCredentials function, it says to call a checkForValidCreds function:
// Optional
// Check if the provided username and token are valid through a
// call to your service. You would have to have a `checkForValidCreds`
// function defined for this to work.
var validCreds = checkForValidCreds(username, token);
Meanwhile, in the isAuthValid function, you are asked to define a similar function called validateCredentials:
// This assumes you have a validateCredentials function that
// can validate if the userName and token are correct.
return validateCredentials(userName, token);
Are these user-defined functions different from each other? If so, what are the differences I need to know when defining them?
The only function names that need to be constant are listed in the auth page. Related to your question, as long as you define isAuthValid(), you should be good to go. checkForValidCreds and validateCredentials are just two implementation of the same method used inside isAuthValid(). You can name these anything as long as they are correctly referenced within isAuthValid().
Google provides an example of optimising a custom formula to recurse over an array where there is one. It helps with the whole efficiency thing The example provided from the Apps Script Page shows the an example where there is 1 parameter, as:
function DOUBLE(input) {
if (input.map) { // Test whether input is an array.
return input.map(DOUBLE); // Recurse over array if so.
} else {
return input * 2;
}
}
What if there are 2 or more parameters? How can we still recurse?
Thank you for your responses. The post to which TheMaster refers is probably right, but to be honest, I don't completely understand it. I have resolved my issue by referring back to Google's pages again. To add further to Ghost's question, I'd like to have one parameter as an array and other other a single constant - sorry for the confusion, I'll be sure to put more thoughts into my future questions.
My updated prototype formula looks like this:
function DOSOMETHING(x, y) {
return Array.isArray(x) ?
x.map(i => i.map(cell => cell * y)) :
x * y;
}
Happy to report it actually does what I want it to. x can be a single number or an array. y can only be a single integer in this case.
Since my original post, Google updated their pages to show the function using ternary operator, which made it easier for me to read & understand.
I keep seeing this kind of thing in the library I'm currently working with:
/**
* Builds a pie chart from data in a specific column.
*
* #param {Object[][]} data a JavaScript 2d array
* #param {int} columnIndex the index of the column from which the chart should be created
* #param {int} optfilter the index of the column used to add a filter to the chart
* #return {ChartPanel} a panel containing chart & filter.
*/
I saw something along the lines that it was helping with autocomplete in the code that the library is being used. What does it do and how does it work?
There are tools that can build documentation from comments if you follow a specified syntax.
#param begins a line that documents parameters to the function, #return documents the expected return value with type and description.
You're right. These things are part of JavaDoc and provide documentation for functions (and properties and classes). Similar to Intellisense pop-up hints on .NET.
An example what this would look like in eclipse:
(Image Source)
They are part of automatic documentation system with which one could quickly fetch an information about function's parameters and return types.