I am currently trying to write a script to compare two ranges in Google Sheets. One is a list of emails to contact in (sht1), and the other is a list of emails on a blacklist in (sht2). I want to automate a script that will compare each email in the contact list in sht1, with ALL of the emails in the blacklist of sht2. If the value in sht1 at point [i] matches any of the values in the blacklist in sht2[j], from sht2[0] to sht2[length] it will automatically cause the row in sht1 to delete at [i].
However, the script is not working. I keep getting the error "range out of bounds" I have tried changing the conditions of i from if(var i=0;i
Any help would be great appreciated.
function DeleteDupes()
{
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sht1=ss.getSheetByName('Email List All');
var dd = SpreadsheetApp.openByUrl('noneofyourbusiness')
var sht2=dd.getSheetByName('Sheet1');
var rng1=sht1.getRange('A:A');
var rng2=sht2.getRange('A:A');
var rng1A=rng1.getValues();
var rng2A=rng2.getValues();
for(var i=rng1A.length;i>=0;i--)
{
for(var j=0;j<=rng2A.length;j++)
{
if(rng1A[i]==rng2A[j]);
sht1.deleteRow(i-1);
break;
}
}
}
Thank you
function DeleteDupes(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sht1=ss.getSheetByName('Email List All');
var dd = SpreadsheetApp.openByUrl('noneofyourbusiness')
var sht2=dd.getSheetByName('Sheet1');
var rng1=sht1.getRange('A:A');
var rng2=sht2.getRange('A:A');
// Code changes begin here:
var rng1A=formatRangeValues(rng1);
var rng2A=formatRangeValues(rng2);
// Keep only the emails in `rng1A` that are not in `rng2A`
var filteredEmails = rng1A.filter(function(email){
return this.indexOf(email) === -1;
}, rng2A);
// Remove all emails in `rng1` in preparation of inserting filtered list
rng1.clearContent();
// Protect against an empty `filteredEmails` array
if(filteredEmails.length === 0)
return;
// Convert `filteredEmails` back to a 2D Range
// and insert back into Column A
sht1
.getRange(1, 1, filteredEmails.length)
.setValues(
filteredEmails.map(function(e){
return [e] ;
})
);
}
// Filter out blank elements.
// reduce{concat} flattens the array
function formatRangeValues(range){
return range
.getValues()
.filter(String)
.reduce(function(a,b){
return a.concat(b)
}, []);
}
This will remove any instances of an email on sht1 that are on sht2. It works by bringing the sht1 Range into memory and converting it to an array of values. It then filters the values and inserts them back into sht1.
There are two major advantages of doing the data manipulation in JavaScript instead of on the spreadsheet. First, it is going to be faster. Reads and writes to the spreadsheet document are expensive. This way we can batch them instead of doing them individually. Second, it is safer as it only manipulates the cells in question. We do not need to worry about side effects (such as deleting a row when other columns may someday be used).
Deleting Items on a BlackList
Try this:
function delItemsOnBlacklist() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Contacts');
var sh2=ss.getSheetByName('BlackList')
var rg1=sh1.getRange(1,1,sh1.getLastRow(),1);
var rg2=sh2.getRange(1,1,sh2.getLastRow(),1);
var vA=rg1.getValues();
var vB=rg2.getValues();
var vBlackList=[];
var d=0;
for(var i=0;i<vB.length;i++){
vBlackList.push(vB[i][0]);
}
for(var i=0;i<vA.length;i++)
{
if(vBlackList.indexOf(vA[i][0])>-1){
sh1.deleteRow(i-d+1);
d++;
}
}
}
I changed your file names and range descriptions a bit so make sure you make the appropriate changes. I prefer to put the blacklist into an array. It just seems neater to me. If you keep track of the deleted items as in "d" and add that to the deleteRow then you can increment in the loop which I think were more use to doing.
Related
To explain the larger context: there are several forms which generate different sheets. I'm looking for a way to conditionally copy some of the responses sheet to a seperate "Overview" document. Code-wise, I had some ideas for the Overview document, but stranded near the start.
My method was going to be to build functions for all the information I want to retrieve, such as date of birth (example in code block below), date of submission and phone number, when I click on a button. The information may only be copied if the first and surname match the ones in the Overview. The order of the sheets in different docs are not the same and the column length is continually in flux. Furthermore, the amount of rows in the Overview doc is different than the form submission sheets.
In other words: if Anne Annenson would be the twenty-first respondent to a form, I want that information in the overview sheet where they are the first person.
function getDobs() {
var targetSpreadsheet = SpreadsheetApp.getActive();
var targetSheet = targetSpreadsheet.getSheetByName("Overview");
var targetFirstNameCheck = targetSpreadsheet.getRange("A4:A");
var targetSurnameCheck = targetSpreadsheet.getRange("B4:B");
var sourceSpreadsheetDob = SpreadsheetApp.openById("...");
var sourceDob = sourceSpreadsheetDob.getSheetByName("Form responses 1");
var sourceFirstNameCheckDob = sourceSheetDob.getRange("C2:C");
var sourceSurnameCheckDob = sourceSheetDob.getRange("D2:D");
var sourceRangeDob = sourceSheetDobConsent.getRange("E2:E");
if (sourceFirstNameCheckDob==targetFirstNameCheck && sourceSurnameCheckDob==targetSurnameCheck){ //then I want to copy the data
var sourceData = sourceRangePronouns.getValues();
var targetRangeDob = targetSheet.getRange("C4:C");
}
else (//I want it to leave the cells alone, so any text or formatting that might have been put in manually is still there.){
}
}
I would like for the responses to remain in the form response sheets as well.
Any thoughts?
Cooper already explained all the things you need in the comments. And below is what your code would look like following Cooper's comments.
Code
function getDobs() {
var targetSpreadsheet = SpreadsheetApp.getActive();
var targetSheet = targetSpreadsheet.getSheetByName("Overview");
var targetLastRow = targetSheet.getLastRow();
// range equivalent to A4:B
var targetNamesCheck = targetSheet.getRange(4, 1, targetLastRow - 3, 2).getValues();
// tested in same spreadsheet, change "targetSpreadsheet" to openById on your actual script
var sourceSpreadsheetDob = targetSpreadsheet;
var sourceDob = sourceSpreadsheetDob.getSheetByName("Form responses 1");
var sourceLastRow = sourceDob.getLastRow();
// range equivalent to C2:D
var sourceNamesCheckDob = sourceDob.getRange(2, 3, sourceLastRow - 1, 2).getValues();
// range for data to be copied (E2:G in my sample data)
var sourceRangeDob = sourceDob.getRange(2, 5, sourceLastRow - 1, 3).getValues();
var output = [];
targetNamesCheck.forEach(function (targetNames) {
// search sourceNamesCheckDob for targetNames
var index = searchForArray(sourceNamesCheckDob, targetNames);
// if targetNames is in sourceNamesCheckDob, save the data on that row for later
if (index > -1)
output.push(sourceRangeDob[index]);
// append blank cells if data is not found
else
output.push(new Array(sourceRangeDob[0].length));
});
// if there were names that were found, write the data beside the targetNames
if (output.length > 0) {
targetSheet.getRange(4, 3, output.length, output[0].length).setValues(output);
}
}
// function to search the array for the object
function searchForArray(haystack, needle) {
var i, j, current;
for(i = 0; i < haystack.length; ++i) {
if(needle.length === haystack[i].length) {
current = haystack[i];
for(j = 0; j < needle.length && needle[j] === current[j]; ++j);
if(j === needle.length)
return i;
}
}
return -1;
}
Overview:
Form responses 1:
Overview after running getDobs:
EDIT:
Since there are no methods that includes the apostrophe when the cell value is being fetched, easiest way is to have the sheets identify the phone number as text so it won't remove the 0 in the beginning. I've thought of 3 ways to have the 0 included in the output:
Add the apostrophe manually on the specific output column via script
Add dashes on the number so it is treated as text (09395398314 -> 093-9539-8314) (just an example, not sure if that is the right format)
Format the output column into number -> plain text instead of number -> automatic
I prefer formatting the output column as that will be the fastest and easiest thing to do.
Format:
Output:
Note:
This function will fill up rows where names in Overview are present in Form responses 1.
References:
Check whether an array exists in an array of arrays?
javascript create empty array of a given size
I have an html form where the client's data is inserted in and it appends row with the values on to a google sheet.
In the form, there's a field that searches and returns the clients data when searching for a specific value (id number).
function getID(IDsearch){
var ws = SpreadsheetApp.getActiveSheet();
var data = ws.getRange(3, 1, ws.getLastRow(), 36).getValues();
var dataInput = data.map(function(r){return r[7];});
var position = dataInput.indexOf(IDsearch);
var dataArray = ws.getRange(position+3, 1, 1, 36).getValues();
if(position > -1){
return dataArray;
} else {
return position;
}
}
After this runs, all the input fields in the form are populated with the data from that row.
I need to edit the values in the form and when submit it should overwrite/update the existing row with that id number.
In google sheets documentation, I've found the spreadsheets.values.update method, but I cannot figure this out. I'm pretty new in this and any help would be appreciated.
Thanks everyone!
You want to achieve the following flow.
Input "ID" to id="insertID" and click "Search by ID".
Show the values from Spreadsheet by searching "ID".
Edit the values of id="name" and id="ID".
When "Save data" is clicked, you want to update the values on the Spreadsheet.
From your replying, shared Spreadsheet and script, I could understand like above. If my understanding is correct, how about ths following modification? Please think of this as just one of several possible answers.
Modification points:
In your case, processForm at Google Apps Script side is required to be modified.
Search the row using formObject and overwrite the values of cells.
Modified script:
When your script is modified, please modify processForm at Google Apps Script side as follows. I remove the Spreadsheet ID from the URL. So please set it, before you test the script.
function processForm(formObject) {
var url = "https://docs.google.com/spreadsheets/d/###/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Database");
// I added and modified below script.
var ranges = ws.getRange(4, 2, ws.getLastRow() - 3, 1).createTextFinder(formObject.ID).findAll();
if (ranges.length > 0) {
for (var i = 0; i < ranges.length; i++) {
ranges[i].offset(0, -1, 1, 2).setValues([[formObject.name, formObject.ID]]);
}
} else {
ws.appendRow([formObject.name, formObject.ID]);
}
}
In this modification, when the same IDs are existing, all rows of the same IDs are overwritten. For example, if you want to modify the 1st one, please modify to ranges[0].offset(0, -1, 1, 2).setValues([[formObject.name, formObject.ID]]);.
Reference:
Class TextFinder
Try this:
function getID(IDsearch){
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();//dont know what the sheet is
var rg=sh.getRange(3,1,sh.getLastRow()-2,36);
var data=rg.getValues();
var idA=sh.getRange(3,8,sh.getLastRow()-2,1).getValues().map(function(r){return r[0];});//it looked like column 8 was your id column
var idx=idA.indexOf(IDsearch);
if(idx>-1) {
return ws.getRange(pos + 3,1,1,36).getValues()[0];//flattened the row to a 1d array
}else{
return idx;
}
}
#dianadfonseca, as #Tanaike points out, without more detail about your data structure, people will be speculating in order to answer your question. As I will be...
Please read the following answer, and tailor it to your needs if it works for you.
Example:
function getRow(id){
var ws = SpreadsheetApp.getActiveSheet();
// Number of headers to skip
var numHeaders = 2;
// the starting row
var startRow = numHeaders + 1;
// The column where the IDs are is known
var idCol = 8;
// The number of rows with data not headers
var numRows = ws.getDataRange().getLastRow() - numHeaders;
// An array with the ids to find a match in
// getRange() returns a 2D array, so you can transpose it to flatten it
var ids = ws.getRange(startRow,idCol,numRows).getValues();
ids = transpose(ids)[0];
// Get the index where id matches in ids
var row = ids.indexOf(id);
// If there's a match
if(row > -1){
// Correct row indexing
row = row + startRow;
}
return row;
}
function updateRow(row,data){
var ws = SpreadsheetApp.getActiveSheet();
// The column for each property is known
var propertyOneCol = 1;
// Update property using setValue()
ws.getRange(row,propertyOneCol).setValue(data.propertyOne);
// And so on...
}
// Transpose to avoid looping through the array
function transpose(a)
{
return Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
}
You can take a look the spreadsheet used for this example here with its bound script to play around.
Here is the function I used for testing
function test(){
// You are receiving this from your form
var data = {"propertyOne":"Juan","propertyTwo":20, "id":123467};
var id = data.id;
updateRow(getRow(id),data);
}
I am trying to achive the following. In google sparesheet I have one sheet with values "AllValues", in another sheet "Randomvalues" I would like to get random values from sheet "AllValues".
I have tried two options, first I tried randbetween formula:
=INDEX(AllValues!A4:A103,RANDBETWEEN(1,COUNTA(AllValues!A4:A103)),1)
It is working, but it refresh/recalculate new values all the time column is changed. Googeled a lot and seems that there is not much to do to freeze already calculated results.
Next I tried function:
function random() {
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ss = sss.getSheetByName('Values'); //the sheet that has the data
var range = ss.getRange(1,1,ss.getLastRow(), 4); //the range you need: 4 columns on all row which are available
var data = range.getValues();
for(var i = 0; i < data.length; i++)
{
var j = Math.floor(Math.random()*(data[i].length)); //method of randomization
var element = data[i][j]; // The element which is randomizely choose
ss.getRange(i+1, 6).setValue(element);
}
}
But this function is not working for me, google sparesheet gives error on line 11, that setVaue is not allowed.
Line 11: ss.getRange(i+1, 6).setValue(element);
Googled this one too, there are lot of suggestion, but I am not very familiar with functions, I did not managed to get it working.
Hope that someone can help me out.
Using a formula assumes repeated calculations usually. You cannot prevent them and only can try to return old values instead. This task is not trivial, since any formula cannot refer to the same cell where the result is to be returned (a circular reference occurs). Do not use formulas for single time calculation.
On the other hand, using a script function makes it possible to generate required data directly and only once or on demand. I think, the function below will help you to understand all the neccesary steps for sample source and target ranges.
function random() {
var source = "AllValues!A4:A103",
target = "RandomValues!F2:F22";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceValues = ss.getRange(source).getValues(),
targetRange = ss.getRange(target),
targetValues = [];
while (targetValues.length < targetRange.getHeight()) {
var randomIndex = Math.floor(Math.random() * sourceValues.length);
targetValues.push(sourceValues[randomIndex]);
}
targetRange.setValues(targetValues);
}
You can run it manually or choose a proper trigger.
There are multiple ways of achieving this goal.
Custom Menu
As mentioned by #Tanaike, you can avoid the recalculation and the formula dependency by using a Custom Menu:
// #OnlyCurrentDoc
// Create a function that binds the "simple trigger" for the open event:
function onOpen(e) {
// Add a menu to the UI with the function we want to be able to invoke.
const ui = SpreadsheetApp.getUi();
ui.createMenu("Randomizer")
.addItem("Sample from 'AllValues' sheet", "sampleAllValues")
.addToUi();
}
You then need a function definition matching this name sampleAllValues, and when the user selects the associated menu option, it will be invoked with the permissions of the clicking user (the user will be prompted first to provide consent for access per the script's OAuth scopes).
function sampleAllValues() {
const wb = SpreadsheetApp.getActive();
const destination = wb.getSheetByName("RandomValues");
const source = wb.getSheetByName("AllValues");
if (!source || !destination)
throw new Error("Missing required sheets 'RandomValues' and 'AllValues'");
// Create a flat array of all non-empty values in all rows and columns of the source sheet.
const data = source.getDataRange().getValues().reduce(function (compiled, row) {
var vals = row.filter(function (val) { return val !== ""; });
if (vals.length)
Array.prototype.push.apply(compiled, vals);
return compiled;
}, []);
// Sample the smaller of 50 elements or 10% of the data, without replacement.
const sample = [];
var sampleSize = Math.min(50, Math.floor(data.length * .1));
while (sampleSize-- > 0)
{
var choice = Math.floor(Math.random() * data.length);
Array.prototype.push.apply(sample, data.splice(choice, 1));
}
// If we have any samples collected, write them to the destination sheet.
if (sample.length)
{
destination.getDataRange().clearContent();
// Write a 2D column array.
destination.getRange(1, 1, sample.length, 1)
.setValues(sample.map(function (element) { return [ element ]; }));
// Write a 2D row array
// destination.getRange(1, 1, 1, sample.length)
// .setValues( [sample] );
}
}
Custom Function
If you still wanted to use a custom function from the RandomValues sheet, e.g.
RandomValues!A1: =sampleAllValues(50, AllValues!A1:A)
then you would need to return sample instead of write to a specific sheet. Note that custom functions are treated deterministically--they are computed at the time of entry and then only recalculated when the values of their arguments change. Custom functions run with very limited scope, so be sure to review their restrictions.
The above usage hints that you might find it useful to allow passing in the number of desired samples, and the values to sample from:
function sampleAllValues(sampleSize, value2Darray) {
const data = value2Darray.reduce(function (compiled, row) {
/* as above */
}, []);
/* sample as above */
return sample; // Must be 2D row or 2D column array, or a single primitive e.g. `1`
}
No matter which route you take, be sure to review your script's error logging by viewing your script's Stackdriver logs. (View -> Stackdriver Logging)
References:
Sheet#getRange
Custom functions
Custom menus
Array#reduce
Array#map
Array#splice
.push.apply
I found something online and it works for changing text to title case; however, it only works on adjacent columns and I need to apply this to multiple nonadjacent columns. I will paste in the script. If you can give a fix to being able to take care of letting the script run on multiple nonadjacent columns that I specify, that would be great. I found some stuff online that says it can do that, but it is not clear to me.
Here is the script that works:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange("A_range");
range.activate();
var values = range.getValues();
if (values.map) {
range.setValues(values.map(function(row) {
return row.map(titleCase);
}));
}
else {
range.setValue(titleCase(values));
}
}
function titleCase(str) {
return str.toString().split(/\b/).map(function(word) {
return word ? word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() : '';
}).join('');
}
This was from other people on this concept:
The stuff to make it run on other columns is this:
var range1=sheet.getRange("A1:A19").getValues();
var range2=sheet.getRange("C1:C19").getValues();
var range=[],i=-1;
while ( range1[++i] ) {
range.push( [ range1[i][0], range2[i][0] ] );
}
where range will have content from both columns.
data = sheet.getRange("A1:C19").getValues();
for (i = 0; i < data[0].length; i++) {
// do something with data[0][i]
// do something with data[2][i]
}
I am not sure how to implement these 2 other ideas listed above. If you could be really specific, like actually put something into the first script that lets it run on Col. A and Col. D,for example, it would be much better than generalities, as I am really really new to this and have spent an enormous amount of time trying to learn it/get a handle on it. Thanks!
Because making as few calls as possible to the SpreadsheetApp API is better for speed, i'd prefer to simply take a Range of all the cells between the first and the last column, apply the transformation to selected columns and then write the whole lot back again. The only place to edit then if the columns change is a single pattern array.
var columnPattern = [1,5,6,7] // Equivalent to [A,E,F,G]
The script then runs a simple map over the two-dimensional array representing the sheet.
function transform() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// Rows (Could be first row and last row, OP isn't clear.
var startRow = 1, endRow = sheet.getLastRow();
// An array with Column indexes for those you want in Title Case.
var columnPattern = [1,5,6,7];
var firstColumn = parseInt(columnPattern.slice(0,1));
var lastColumn = parseInt(columnPattern.slice(-1));
// The whole range
var range = sheet.getRange(startRow,
firstColumn,
endRow,
lastColumn - firstColumn)
// Apply Title Case to selected columns.
var data = range.getValues().map(function(row, i, rows) {
row = row.map(function(col, j, row) {
if(columnPattern.indexOf(j + firstColumn) >= 0) {
col = titleCase(col);
}
return col;
});
return row;
});
range.setValues(data);
}
The only point I'd perhaps clarify is the line where it identifies the columns to amend.
if(columnPattern.indexOf(j + firstColumn) >= 0) {
This just corrects for the columnPattern array not being the same dimension as your sheet. An alternative would be to have an array that did match the x-dimension with boolean values, but this would be less adaptable to your sheet changing size.
I'd resist putting this in an onEdit() function but it depends on your use case as to how often data changed.
Is it possible to get the filter criteria of my data set. For example, if one of my column is "Department" and I filtered the data to display only "IT". How do we get the filtered criteria, "IT". I need to get that filtered criteria into my GAS to do some other manipulation.
Thanks.
Try this example for a simple filter. It will filter information (change XXX to something meaningful) from the first column:
function onOpen(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var filterMenuEntries = [{name: "filter Column1", functionName: "filter"}];
ss.addMenu("choose filter", filterMenuEntries);
}
function filter(){
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i=1; i <=numRows -1; i++){
var row =values[i];
// Column value
var myValue = row[0];
// filter value
if (myValue == "XXX"){
sheet.hideRows(i+1);
}
}
}
Google Spreadsheet already has a FILTER formula (I always use this page to remind me how to do it). So for example if your data looked like this
A
1 Department
2 IT Department (Edinburgh)
3 Department of IT
4 Other Department
to get a filtered list you could use the formula
=FILTER(A:A;FIND("IT",A:A)>0)
(Working example here)
If you want to do something entirely in Apps Script Romain Vialard has written a Managed Library with a filter function. Here are instructions for installing and using the 2D Array2 library
Filters are now available using with the recent launch of the Advanced Sheets Service:
Here is a link to the article
Here is a little snippet of how to do :
function setSheetBasicFilter(ssId, BasicFilterSettings) {
//requests is an array of batchrequests, here we only use setBasicFilter
var requests = [
{
"setBasicFilter": {
"filter": BasicFilterSettings
}
}
];
Sheets.Spreadsheets.batchUpdate({'requests': requests}, ssId);
}
In search for an answer to this question, I came up with the next workaround:
apply filters in the sheet;
color the filtered (and therefore
visible) cells (in the example code red in column F);
run script:
reading background colors (of column F) in array colors
iterating this array
building up the new array of row numbers that are visible.
This last array can be used to further manipulations of the sheet data.
To vivualize the effect of the script, I made the script setting the background of used cells to green.
It's one extra small effort for the end user to make, but IMHO it's the only way to make it possible to only use the filtered data.
function getFilterdData(){
var s = SpreadsheetApp.getActive();
var sheet= s.getSheetByName('Opdrachten en aanvragen');//any sheet
var rows = new Array();
var colors = sheet.getRange(1, 6, sheet.getLastRow(), 1).getBackgrounds();
for(var i = 0; i < colors.length; i++){
if(colors[i] == "#ff0000"){
var rowsIndex = rows.length;
rows[rowsIndex] = i+1;
sheet.getRange(i+1, 6).setBackground("#d9ead3")
}
}
}
Based on mhawksey answer, I wrote the following function:
//
function celdasOcultas(ssId, rangeA1) {
// limit what's returned from the API
var fields = "sheets(data(rowMetadata(hiddenByFilter)),properties/sheetId)";
var sheets = Sheets.Spreadsheets.get(ssId, {ranges: rangeA1, fields: fields}).sheets;
if (sheets) {
return sheets[0].data[0].rowMetadata;
}
}
then called it this way:
// i.e. : ss = spreadsheet, ranges = "Hoja 2!A2:A16"
Logger.log(range.getA1Notation());
var ocultas = celdasOcultas(ss.getId(), sheetName + "!" + range.getA1Notation());
// Logger.log(ocultas);
var values = range.getValues();
for (var r = 0; r < values.length; r++) {
if (!ocultas[r].hiddenByFilter) {
values[r].forEach( function col(c){
Logger.log(c);
});
}
//range.setBackground("lightcoral");
}
Than prevent the log of the hidden rows. You can see it in action at:
Prueba script, note project has to get Google Sheets API enabled on Google API Console.
Hope it helps. Thank you!
This as raised in Google's Issue Tracker as Issue #36753410.
As of 2018-04-12, they have posted a solution:
Yesterday we released some new functionality that makes it possible to
manipulate filters with Apps Script:
https://developers.google.com/apps-script/reference/spreadsheet/range#createFilter()
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getfilter
https://developers.google.com/apps-script/reference/spreadsheet/filter
Unfortunately Apps Script still doesn't have methods for determining
if a given row or column is hidden by the filter. See comment #157 for
a workaround utilizing the Sheets Advanced Service.
The Filter class lets you get the FilterCriteria for each column in turn.