How to Put Formula Count another sheet in App Script - google-apps-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)

Related

Google Apps script on cell background change recalculation [duplicate]

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.

Google Script - Clear Range except one specific value

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()

Values from Ranges by named ranges to variables

I have many named ranges in Spreadsheet.
I need get values from areas by current row (e) and current column by namedranges and write it to variables. I write a lot of the same script
function sendEmailClerck(e){
var sheet = SpreadsheetApp.getActive().getSheetByName('Ответы на форму (1)');
var idRow = e.range.getRow();
var name = sheet.getRange(idRow, SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ReqUser').getColumn()).getValue();
var cli = sheet.getRange(idRow, SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ReqCLI').getColumn()).getValue();
var priority = sheet.getRange(idRow, SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ReqPriority').getColumn()).getValue();
var date = sheet.getRange(idRow, SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ReqDateStart').getColumn()).getValue();
var date2 = sheet.getRange(idRow, SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ReqDateCloseU').getColumn()).getValue();
var tip = sheet.getRange(idRow, SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ReqType').getColumn()).getValue();
var users = sheet.getRange(idRow, SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ReqAddress').getColumn()).getValue();
I found one script in this site but I can't understand how can I use it here
function getRangeName(A1Notation) {
var rangeName = "";
//get all the name ranges of the sheet
var namedRanges = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getNamedRanges();
// loop on all the names range of the sheet
for(i=0;i<namedRanges.length;i++){
//if it's the same A1 Notation
if(namedRanges[i].getRange().getA1Notation() == A1Notation)
rangeName = namedRanges[i].getName();
}
//will return the name of the range or "" if there is no range
return rangeName;
}
I think that's it I need. I think I can write to variables the values by put the value in parameter and send it to one function and return it back. Can you help me this this?
Thank you for your help!
Try Array.map() to make this a bit more manageable, like this:
function sendEmailClerck(e) {
const sheet = SpreadsheetApp.getActive().getSheetByName('Ответы на форму (1)');
const rangeNames = [
'ReqUser',
'ReqCLI',
'ReqPriority',
'ReqDateStart',
'ReqDateCloseU',
'ReqType',
'ReqAddress',
];
const [name, cli, priority, date, date2, tip, users] = rangeNames.map(rangeName => {
const column = sheet.getRange(rangeName).getColumn();
return sheet.getRange(e.range.rowStart, column).getValue();
});
}
You may want to rethink the whole approach though. The code is very inefficient because it reads every cell one by one. You should probably use just one sheet.getDataRange().getValues() call and parse the data from the the 2d array it returns.
Some of the best resources for learning Google Apps Script include the Beginner's Guide, the New Apps Script Editor guide, the Fundamentals of Apps Script with Google Sheets codelab, the Extending Google Sheets page, javascript.info, Mozilla Developer Network and Apps Script at Stack Overflow.

Google App script sheets - How to get the highlighted range and write data to it

I would like to write data that is generated from my own function to the highlighted cell of the user. It should be able to take single cell and multiple cell highlights.
if(isNaN(text))
ui.alert('Input is not a number');
else{
var sheet = SpreadsheetApp.getActiveSheet();
var activeRangeList = sheet.getActiveRangeList();
if(activeRangeList==null)
ui.alert('No range selected');
else{
var rgMyRange = sheet.getRange("B1:B10");
rgMyRange.setValue(myFunc(NumberOfRowsHighlighted,parseInt(text)))
}
}
I would like for the user to highlight a range, when the scripts run i would need to get the number of rows highligted along with the input number to be passed to myfunc. The function always generates an array the same size as the number of highlighted rows. Finally i would like to be able to write back to the highlighted cell using the array return by the ffunction.
sample return of myfunct
[1,2,3,...,100]
I am having a hard time reading the documentation since google doesnt really define what is a Range or a RangeList and an example of the data returned. So if anybody can help me it would be greatly appreciated.
I have solved it, using different objects and functions, it is somewhat similar to the first one
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getSelection();
if(selection.getActiveRange().getA1Notation()==null)
ui.alert('No range selected');
else{
var range = SpreadsheetApp.getActiveSpreadsheet().getRange(selection.getActiveRange().getA1Notation());
range.setValues(GENERATEGROUP(10,parseInt(text)));
}
I'm not sure what the function GENERATEGROUP(10,parseInt(text)) outputs but here's a solution that writes to Row and Column numbers each cell in the active range (i.e. the users selection)
//es6-V8
function writeActiveRange() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getActiveSheet();
const rg=sh.getActiveRange();
let v=rg.getValues();
const row=rg.getRow();
const col=rg.getColumn();
v.forEach(function(r,i){r.forEach(function(c,j){v[i][j]=`Row:${row+i},Col:${col+j}`;});});
rg.setValues(v);
}

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.