How to pass a range into a custom function in Google Spreadsheets? - function

I am trying to compare two google sheets (for duplicate values) over a predefined range..
I am aware that google lets you compare sheets via tools, but i need a more customized comparison hence the function.
I want to create a function which takes in a range... something like:
function myFunction(range) {
var firstColumn = range.getColumn();
// loop over the range
}
The problem is that when I try doing this, the parameter seems like it is only passing the values to the function. Thus, I cannot use any of the Range methods such as getColumn(). When I attempt to do so, it gives the following error:
error: TypeError: Cannot find function getColumn in object 1,2,3.
How can I send an actual range rather than just the values to one of my custom functions?
Note- Range in my case is the entire sheet (both of them that need to be compared).
Thanks!

Custom functions arguments are calculated before being passed to the code of the custom function so range will be a single value or an array of values.
You could pass a reference to a range as a string, i.e. =myFunction("Sheet!A:Z"), then use something like the following:
function myFunction(reference) {
var range = SpreadsheetApp.getActiveSpreadsheet().getRangeByName(reference);
var firstColumn = range.getColumn();
// do something
}
Note: getRangeByName work both with named ranges and references os it could be a better choice than getRange.

Google Script treats ranges as Arrays. So, you could better work with entered range as you work with an array. Here's good technique how to loop through an array:
https://stackoverflow.com/a/14991272/5372400

In your cell, you would do
=myFunction("A1")
Then in your code
function myFunction(ref)
{
var range = SpreadsheetApp.getActiveSpreadsheet().getRangeByName(ref);
// do something with range
}
getRangeByName is no longer used.
Source: https://developers.google.com/apps-script/reference/spreadsheet/sheet#getrangea1notation

Related

Google App Script - getFilter and getRange does not return desired filtered results

I have a filter applied to a sheet. I want to return just the data from the filter and not the entire range of the sheet.
const sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(SHEET_NAME)
const filter = sheet.getFilter();
// This returns the entire sheet's range rather than the filtered range.
const range = filter.getRange().getValues();
Based on the code above, why aren't I getting the desired behaviour according to docs from Google?
I had a similar issue with returning filtered values and realized it is the most convenient to make a code that will skip FilterCriteriaBuilder class with functions.
For that reason I developed a code on this link.
https://github.com/NikolaPlusEqual/GoogleAppsScriptFilters/blob/main/Functions
Simple example for using functions from the repository:
function example(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ss1 = ss.getSheetByName("Sheet1");
var range = ss1.getRange(1,1,ss1.getLastRow(),5).getValues();
output = whenNumberGreaterThan(10, range, 3);
Logger.log(output);
}
whenNumberGreaterThan(number, rng, col) is the function from the repository, which will return all values from the rows in the range, that have satisfied "number greater than 10" criteria in the 3rd column of the range. The function is the alternative for FilterCriteriaBuilder Class function whenNumberGreaterThan(number) on this link.
Just copy entire Functions file code into you .gs file and call desired function.
You can get just the values the active filter displays by removing the values in the data that are in rows where isRowHiddenByFilter(rowPosition) returns true.
That said, using JavaScript's Array.filter() would offer much better performance than making multiple API calls to find which rows are hidden.

App Script Conditional Formatting to apply on sheet by name

I have been trying to make a Google App Script code which highlight the cell if it has specific text like "L".
I have made a below code but its not working and when i run this no error appears. i do not know what is the problem.
Can you have a look at it, please that why its not working.
function formatting() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dec');
var range = sheet.getRange("C:AG");
if (range == 'L') {
ss.range.setBackgroundColor('#ea9999');
}
}
Issues with the code:
Three things to mention:
range is a range object, not a string. In the if condition you are comparing an object of type range with an object of type string. You need to use getValue to get the values of the range object and then compare that with the string L.
This code will take a lot of time to complete because you have a large range of cells you want to check but also you are iteratively using GAS API methods. As explained in Best Practices it is way more efficient to use batch operations like getValues,
getBackgrounds and setBackgrounds.
Another improvement you can make is to use getLastRow to restrict the row limit of your range since you are looking for non-empty values. There is no reason for checking empty cells after the last row with content.
Google Apps Script Solution:
function formatting() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dec');
const range = sheet.getRange("C1:AG"+sheet.getLastRow());
const values = range.getValues();
const bcolors = range.getBackgrounds();
const new_bcolors = values.map((r,i)=>r.map((c,j)=>c=='L'?'#ea9999':bcolors[i][j]))
range.setBackgrounds(new_bcolors)
}
Google Sheets Solution:
Another idea would be to just create a conditional formatting in Google Sheets:
and specify a custom color with your hex code:
JavaScript References:
map
ternary operator

get range from google sheets using r1c1

I want to get range selected from r1c1 r2c2
I have
var rc1 = '0:0-3:3';
//considering the string
var res = ss.getActiveSheet().getRange('R0C0:R3C3');
ss.getActiveSheet().setActiveSelection(res);
I get error Range not found
Your range is not found cause you trying to get element out of the table. R0C0 does not exist, R1C1 is the first existing element.
If you want to get range selected from R1C1 to R2C2, you've got
function getWithRCNotation(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var res = ss.getActiveSheet().getRange('R1C1:R2C2');
ss.getActiveSheet().setActiveSelection(res);
}
Make sure that you follow the instructions in this documentation. Based from this thread, when you pass a range, a1:d12 for example, to a function in gSheets, the values of the cells in that range are passed as an array. Try to pass it as a string, like =function("a1:d12"), and deal with the string notation for a range, ...getRange(stringargument).

Google Sheet Custom Function Not Recaculating

Hi I have read about the caching issues with custom functions in Google Sheets, but I'm trying to understand why the following will successfully update if a cell is changed:
function doob(input){
return input * 2;
}
but this will not update:
function doob(input){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var range = sheet.getRange(input);
var values = range.getValues();
return values[0][0] * 2 ;
}
I need to get the range in order to use the .getBackgroundColor() of each cell.
Probably because in the first function you would pass the cellreference directly,
=doob(A1)
and in the script the value of that cell is used.
In the second, you would probably have to pass the range as string (since you want to get the backgroundcolors, so you are not after the values of that range, right ?)
=doob("A1:B8")
As you know, custom functions suffer from memoization. To work around that you could pass in the range a second time, without the quotation marks.
=doob("A1:B8", A1:B8)
That second paramater is a 'dummy' paramater as the script does nothing with it. BUT: any change in values in that range should make the custom function re-evaluate. However I don't know if that is gonna help you a lot if your final goal is to get the backgroundcolors.

Google Apps Script to VMerge tables WITH FORMATTING

Does anyone know if there is a Google apps script out there that does what VMerge does but keeps the formatting of the tables being merged together? (in Google Spreadsheets)
VMerge is a script that can be used as a custom formula but a script that I can trigger myself will do just fine too.
Any help would be much appreciated.
VMerge expects arrays-of-values as parameters, and therefore does not know what cells were referenced creating those arrays. When used as a Custom Formula, the sheet parser resolves all range parameters into their values before passing them to VMerge. Additionally, the parameters may be hard-coded or be the result of Queries or other functions that return ranges. Because of this alone, it's not feasible to modify VMerge to copy cell formats to the new merged table.
Complicating things further, Custom Functions cannot modify cells outside of the one they are attached to, they can only return values or arrays of values. From comment in Issue 37:
2) Scripts used as cell functions are not allowed to do complex things
like connect to other APIs or set the values of other cells. Scripts
used as cell functions are only allowed to return a value.
So you're going to have to settle for a function you call from scripts. The following function will join multiple ranges into a new table at a given anchor point. Because I started out trying to make this a custom function callable from a sheet, the parameters are string expressions of ranges, in a1Notation. (It could easily be refactored to deal directly with Range objects.)
The "Anchor" for the new range is expected to be a cell. One or more ranges of any size may be joined - each will be positioned directly below the previous.
Examples:
VJoin("D1","A1:B"); - All of columns A & B duplicated in columns D & E
VJoin("Sheet2!A1","Sheet1!C9:E10","Sheet1!A14:B15"); - Two different ranges in Sheet 1 joined and copied to Sheet 2.
Here's the code:
/*
* Vertically join the ranges from multiple sources into a new table
* starting at the given anchor point. Values and formatting are copied.
*
* #param {a1Notation} anchorA1 Anchor for joined table.
* #param {a1Notation} sources One or more source ranges.
*/
function VJoin(anchorA1,sources) {
var sheet = SpreadsheetApp.getActiveSheet();
var anchor = sheet.getRange(anchorA1);
var anchorSheet = anchor.getSheet(); // in case anchorA1 is not on the "active sheet"
var nextAnchor = anchor;
for (var i in arguments) {
// Arguments are expected to be Strings, containing a1Notation.
if (i == 0) continue; // First argument was anchorA1, skip it.
if (arguments[i].constructor == String) {
var source = sheet.getRange(arguments[i]);
var destination = anchorSheet.getRange(nextAnchor.getRow(), nextAnchor.getColumn(),
source.getNumRows(), source.getNumColumns() );
// Copy all values & formatting to new location.
source.copyTo(destination);
// Prepare for next range by moving our anchor
nextAnchor = sheet.getRange(nextAnchor.getRow() + source.getNumRows(),
nextAnchor.getColumn());
}
else {
throw new Error ("Expected String containing a1Notation.")
}
}
}
If you need a separate script to bring over the formatting...
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Sheet1');
s.getRange('A1').copyFormatToRange(sheet, column, columnEnd, row, rowEnd);
}
I find the below built in functions to work well pulling information from different Google Sheet files. I have defined named ranges to define what columns to pull into the Master, and also know I am having an issue with Feb.
=sort(arrayformula({
importrange("1sTS3AUfoXqXYrMYJrro9pGEKwqVL_k854yhniNOHNWc","JCJan");
importrange("1ETSD4J-8AI-7pVK0hXJKaWtG3RlHKpnco88Yj8sqNN8","JCFeb")}),1,True)