Google Script - Clear Range except one specific value - google-apps-script

https://docs.google.com/spreadsheets/d/1v4SrxT5voa94gXxjlEtNm69TNTq3cCbRlHymO_5jjic/edit?usp=sharing
I'm having a hard time writing a simple apps script in Google Sheets that clears all values except a specific value. Sample sheet above. The ideal solution would clear all of row "C" or at minimum clear values "C4:C" or "C4:C100" except cells with the word/value "DELIVER". The script below has been working, the problem is that the script needs constant updating as the rows are not fixed and change based on business needs. So a fixed set of ranges is not ideal. Can anyone please point me in the right direction?
function ClearAztecaFOODForm() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRangeList(['C4:C16', 'C19:C28', 'C31:C42']).activate()
.clear({contentsOnly: true, skipFilteredRows: true});
};

Description
You could get column C and replace any value that is not DELIVER with blank. In my example script I get all values from column C starting from row 4 and use an Array method forEach() to replace the values. Then I put column C back.
Code.gs
function ClearAztecaFOODForm() {
try {
let spread = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spread.getSheetByName("COPYAztecaFOOD");
let range = sheet.getRange(4,3,sheet.getLastRow()-3,1)
let values = range.getValues();
values.forEach( row => { if( row[0] !== "DELIVER" ) row[0] = "" } );
range.setValues(values);
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
};
Reference
SpreadsheetApp.getActiveSpreadsheet()
Spreadsheet.getSheetByName()
Sheet.getRange()
Range.getValues()
Range.setValues()
Array.forEach()
try{} catch(){} block
SpreadsheetApp.getUi()

Related

How to insert formula to cell in column with corresponding rownumber?

In a sheet called "WHATMONTH" I need to automatically add a formula via. script to range column G2:G, example
=month(A2)
But adding this to he entire sheet in all rows of column G would still give me A2 / month(A2), that should instead be the corresponding row number, and not 2 in all rows.
Here I am just guessing: =month(A%[rownumber]), something like that.
Is this possible?
For short, this is how you can set the formula =ArrayFormula(MONTH($A$2:$A)) into cell G2 with appscript according to your request:
// But if =ArrayFormula(MONTH($A$2:$A)) could be added with a script - fixed - to G2, then your idea would work, in fact.
const SheetName = 'Your Sheet Name';
const Range = 'G2';
const Formula = '=ArrayFormula(MONTH($A$2:$A))';
function setFormula(){
SpreadsheetApp // this call the Spreadsheet related functions.
.getActiveSpreadsheet() // get spreadsheet_object of the active spreadsheet.
.getSheetByName(SheetName) // get sheet_object of the sheet from the spreadsheet you are working with by sheet name.
.getRange(Range) // get range_object of the range from the sheet you are working with.
.setFormula(Formula); // set a formula into the traget range.
}
In case you need to have more than one Formula to set, you will have to change the scope of the variables:
function run() {
const setFormula = (sheetName,range,formula) => {
SpreadsheetApp
.getActiveSpreadsheet()
.getSheetByName(sheetName)
.getRange(range)
.setFormula(formula);
}
setFormula("your sheet name 1","range 1","formula 1");
setFormula("your sheet name 2","range 2","formula 2");
setFormula("your sheet name 3","range 3","formula 3");
}
this format allows you to edit as many sheets, as many cell, with as many formulas as you want to.
Solved by #Ping. Final working code:
function Setdefault() {
const setFormula = (sheetName,range,formula) => {
SpreadsheetApp
.getActiveSpreadsheet()
.getSheetByName(sheetName)
.getRange(range)
.setFormula(formula);
}
setFormula('FILTERS','$A$2:$A','=CONCATENATE(\"from:(\",(B2), \") subject:\",(C2),\" older_than:\",(D2),\"d\")');
setFormula('FILTERS','$D$2:$D','0');
}

Googlesheet dropdown list to show/hide rows

I want to have various cells that have a dropdownlist to show/hide certain rows
I have the following table
If I select A2 (dropdownlist with AMERICAS_a and AMERICAS_s), with the _s option, I want to hide only rows 3 to 6
If I select A7 (dropdownlist with EUROPE_a and EUROPE_s), with the _s option, I want to hide only rows 8 to 12
I am using an Appscript which works only for 1 of the continent. I want to make it work for any of the Watchcell
function onEdit(a) {
var watchSheet = "Sheet1";
var watchCell = "A2";
var sheet = a.range.getSheet();
if (sheet.getName() !== watchSheet ||
a.range.getA1Notation() !== watchCell) {
return;
}
sheet.hideRows(2, 5);
switch (a.value) {
case "AMERICAS_a":
sheet.showRows(2, 5);
break;
case "AMERICAS_s":
sheet.showRows(2, 1);
break;
default:
}
}
But I am unsure how to add A7 as another watchcell. I have repeated the function onEdit(e) but only the last onEdit function works.
I believe your goal is as follows.
Your Spreadsheet has the data validation rules for several cells in the column "A".
When the value of the dropdown list is changed, you want to run the script.
For example, when the suffix letters of the value of the dropdown list are _s, you want to hide rows until the next dropdown list.
For example, when the suffix letters of the value of the dropdown list are _a, you want to show rows until the next dropdown list.
Modification points:
In your showing script, in the case of both values of AMERICAS_a and AMERICAS_s, the rows are shown.
From your question, I thought that when the position of data validation rules is retrieved, the script might become simple.
When these points are reflected in a sample script, it becomes as follows.
Modified script:
function onEdit(e) {
const sheetName = "Sheet1"; // Please set the sheet name.
const { range, value } = e;
const sheet = range.getSheet();
const row = range.rowStart;
const datavalidations = sheet.getRange("A1:A" + sheet.getLastRow()).getDataValidations();
if (sheet.getSheetName() != sheetName || !datavalidations[row - 1][0]) return;
datavalidations.splice(0, row);
const n = datavalidations.findIndex(([a]) => a);
sheet[value.slice(-2) == "_s" ? "hideRows" : "showRows"](row + 1, n > -1 ? n : datavalidations.length);
}
Testing:
When this script is used, the following result is obtained.
Note:
This sample script is for your sample showing Spreadsheet. So, when you change the structure of the Spreadsheet, this script might not be able to be used. Please be careful about this.
When this script is directly run with the script editor, an error like TypeError: Cannot destructure property 'range' of 'e' as it is undefined. occurs. Please be careful about this. Please change the dropdown list of the column "A".
References:
Simple Triggers
hideRows(rowIndex, numRows)
showRows(rowIndex, numRows)

How to Put Formula Count another sheet in App Script

my First ASK and last at 2020.
Im Newbie with google App Script, need some help
I had 2 Spreadsheet
Spreadsheet ssGETDATA with SHEET1 column(id,name,address,ts,visit)
Spreadsheet ssVERIFY with SHEET1 column (id,ts)
I send data from Android use this code :
function doGet(e) {
return message("Error: Please Try Again");
}
function doPost(e) {
var id= e.parameters.id[0];
var name= e.parameters.name[0];
var address= e.parameters.address[0];
var visit= e.parameters.visit[0];
var ts = Utilities.formatDate(new Date(), "GMT+8", "dd/MM/yyyy HH:mm:ss");
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheets()[0];
sh.appendRow([id,name,address,ts,visit]);
return message("Data Saved");
} else {
return message("Error: Please Try Again");
}}}
function message(msg) {
return ContentService.createTextOutput(msg);
}
I want to Verify data from ssGETDATA but data (id) appeared several times.
so my idea every time append row executed it put formula in column (visit) with =count(id,ssVERIFYSheet1!id) to check it Verified or Not
how it applies in the google app script?
i hope when ssVERIFY changed then ssGETDATA column (visit) counting too.
thanks for your explanation. Happy new Year
The simplest way to check if a column has a value probably is using getValues(), and search for the row in the array:
const range = sheet.getRange('A2:A')
const hasBeenValidated = range.getValues()
.flat()
.some(value => value === id)
if (hasBeenValidated) {
// [...]
}
Notice that I assumed that id is on column A and that it has a header.
flat() is necessary because getValues() returns a 2D array (array of rows) and we only need an array of values.
With this snippet you can check for existing entries at ssGETDATA and/or ssVERIFY before making any changes. It should work for any column but you may need to change the value === id part depending on the type of it (dates, for example).
References
Range getValues() (Google Developers)
Array.prototype.flat() (MDN)
Array.prototype.some() (MDN)
SpreadsheetApp openByUrl(url) (Google Developers)

Looking for better way to pass range for my function sumBlack()

I would love to be able use my script in Google sheet with just =sumBlack(C4:14)
currently my script ** see below ** works in Google sheet with =sumBlack(3,4,14)
3 for column, 4 and 14 are the row start and end
here's my code... pleased that it works though
it sums only cells that have the fontColor of black
function sumBlack(column, rowst, rowend) {
result = 0;
for(row = rowst;row <= rowend;row++){
var txtColor = SpreadsheetApp.getActive().getDataRange().getCell(row, column).getFontColor();
if(txtColor == "#000000"){
result = result + SpreadsheetApp.getActive().getDataRange().getCell(row, column).getValue();
}
}
return result;
}
I believe your goal as follows.
You want to sum the values of cells, when the font color is #000000 as hex.
You want to achieve this using the custom function.
Modification points:
In this case, in order to give the a1Notation to the custom function, how about using =sumBlack("C4:14") instead of =sumBlack(C4:14)? Because when =sumBlack(C4:14) is used, the values of cells "C4:14" is given as 2 dimensional array. By this, the range cannot be known.
In this modification, getFontColors() and getValues() are used instead of getFontColor()andgetValue(), respectively. By this, I think that the process cost will be able to be reduced.
When you can permit this suggestion, how about the following modified script?
Modified script:
When you use this script, please put =sumBlack("C4:14") to a cell. In this case, please don't forget to enclose the a1Notation with ".
function sumBlack(a1Notation) {
const range = SpreadsheetApp.getActiveSheet().getRange(a1Notation);
const fontColors = range.getFontColors();
const values = range.getValues();
const result = fontColors.reduce((n, r, i) => {
r.forEach((c, j) => {
if (c == "#000000") n += Number(values[i][j]);
});
return n;
}, 0);
return result;
}
If you want to give the sheet name like =sumBlack("Sheet1!C4:14"), please modify above script as follows.
From
const range = SpreadsheetApp.getActiveSheet().getRange(a1Notation);
To
const range = SpreadsheetApp.getActiveSpreadsheet().getRange(a1Notation);
In above modified script, when =sumBlack("C4:14") is put and the cell value of "C4:14" is changed, no recalculation occurs. If you want to recalculate for this situation, please add the following script. The following script is automatically run when the cells in the active sheet are edited, and the formula of =sumBlack() is recalculated.
function onEdit(e) {
const sheet = e.source.getActiveSheet();
sheet.createTextFinder("=sumBlack").matchFormulaText(true).replaceAllWith("###sumBlack");
sheet.createTextFinder("###sumBlack").matchFormulaText(true).replaceAllWith("=sumBlack");
}
References:
getFontColors()
getValues()
reduce()
Class TextFinder

Force Google Spreadsheet formula to recalculate

I know this question has been asked before but the answers given are not valid for my case because it's slightly different.
I've created a formula that looks for sheets with a pattern in the name and then uses it's content to generate the output. For example
function sampleFormula(searchTerm) {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheets = spreadsheet.getSheets()
.filter(function(sheet) {
// If sheet name starts with DATA_SHEET_...
return sheet.getSheetName().indexOf('DATA_SHEET_') === 0;
});
const result = [];
sheets.forEach(function(sheet) {
// We get all the rows in the sheet
const rows = sheet.getDataRange().getValues();
rows.forEach(function(row) => {
// If the row it's what we are looking for we pick the columns A and C
if (row[1] === searchTerm) {
result.push([ row[0], row[2] ])
}
});
});
// If we found values we return them, otherwise we return emptry string
return result.length ? result : '';
}
The thing is I need this formula to be re-calculated when a cell in a sheet with a name starting with DATA_SHEET_ changes.
I see most answers (and what I usually do) is to pass the range we want to watch as a parameter for the formula even if it's not used. But in this case it will not work because we don't know how many ranges are we watching and we don't even know the whole sheet name (it's injected by a web service using Google Spreadsheet API).
I was expecting Google Script to have something like range.watch(formula) or range.onChange(this) but I can't found anything like that.
I also tried to create a simple function that changes the value of cell B2 which every formula depends on but I need to restore it immediately so it's not considered a change (If I actually change it all formulas will break):
// This does NOT work
function forceUpdate() {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getActiveSheet();
const range = sheet.getRange(1, 1);
const content = range.getValue();
range.setValue('POTATO');
range.setValue(content);
}
So I don't know what else can I do, I have like a hundred formulas on multiple sheets doing this and they are not updating when I modify the DATA_SHEET_... sheets.
To force that a custom function be recalculated we could use a "triggering argument" that it's only taks will be to trigger the custom function recalculation. This triggering argument could be a cell reference that will be updated by a simple edit trigger or we could use an edit installable trigger to update all the formulas.
Example of using a cell reference as triggering argument
=sampleFormula("searchTerm",Triggers!A1)
Example of using an edit installable trigger to update all the formulas
Let say that formulas has the following form and the cell that holds the formula is Test!A1 and Test!F5
=sampleFormula("searchTerm",0)
where 0 just will be ignored by sampleFormula but will make it to be recalculated.
Set a edit installable trigger to fire the following function
function forceRecalculation(){
updateFormula(['Test!A1','Test!F5']);
}
The function that will make the update could be something like the following:
function updateFormula(references){
var rL = SpreadsheetApp.getActive().getRangeList(references);
rL.getRanges().forEach(function(r){
var formula = r.getFormula();
var x = formula.match(/,(\d+)\)/)[1];
var y = parseInt(x)+1;
var newFormula = formula.replace(x,y.toString());
r.setFormula(newFormula);
});
}
As you can imagine the above example will be slower that using a cell reference as the triggering argument but in some scenarios could be convenient.