function MyFunction() {
for(x=4;x<=45;x++)
{
var sheet = SpreadsheetApp.getActiveSheet().getRange(x, 7).getValue();
var box = SpreadsheetApp.getActiveSheet().getRange(x, 3).getValue();
var box2 = SpreadsheetApp.getActiveSheet().getRange(x, 4).getValue();
if(box == "TRUE" && box2 == "TRUE")
var sum = sheet+50;
else if(box == "TRUE" && box2 == "FALSE")
var sum = sheet+20;
else if(box == "FALSE" && box2 == "TRUE")
var sum = sheet+30;
else
var sum = sheet;
SpreadsheetApp.getActiveSheet().getRange(x, 16).setValue(sum);
}
}
columns 3 and 4 contain checkBoxes the column 7 contain integer values, the idea is to add +50 if both checkBoxes on its row are checked, +20 if only the first one is checked, +30 if the second one is checked and keep the integer column value if none is checked.
You want to retrieve the values from the columns "C", "D" and "G".
The columns "C" and "D" are the checkboxes.
The column "G" is the numbers.
You want to achieve the following conditions.
When the columns "C" and "D" are true and true, you want to add 50 to the value of column "G".
When the columns "C" and "D" are true and false, you want to add 20 to the value of column "G".
When the columns "C" and "D" are false and true, you want to add 30 to the value of column "G".
When the columns "C" and "D" are false and false, you want to use the value of column "G".
You want to put the result values to the column "P".
You want to achieve this using Google Apps Script.
Your issue is that sum is always sheet which is the value of the column "G".
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Pattern 1:
In this pattern, your script is modified.
Modification point:
When the value from a checkbox is retrieved with getValue(), the value is the boolean type. In your script, the string values of TRUE and FALSE are used at the if statement. By this, var sum = sheet; is always run. I think that this is the reason of your issue.
Modified script:
When your script is modified, please modify as follows.
From:
if(box == "TRUE" && box2 == "TRUE")
var sum = sheet+50;
else if(box == "TRUE" && box2 == "FALSE")
var sum = sheet+20;
else if(box == "FALSE" && box2 == "TRUE")
var sum = sheet+30;
else
var sum = sheet;
To:
var sum = sheet;
if(box === true && box2 === true)
sum = sheet+50;
else if(box === true && box2 === false)
sum = sheet+20;
else if(box === false && box2 === true)
sum = sheet+30;
Note:
At Google Apps Script, even when the variable of sum is declared in the if statement, sum can be retrieved at the outside of the if statement. So the script works. But from the scope of variable, I recommend to declare the variable of sum at the out of the if statement like above modified script.
Pattern 2:
In this pattern, your goal is achieved by reducing the process cost from your script. In this case, the values from the columns "C" to "G" are retrieved with getValues(), and the retrieved values are processed. Then, the result values are put to the column "P" with setValues(). In your situation, when getValues(), setValues() and map() are used instead of getValue(), setValue() and the for loop, the process cost can be reduced. Ref1, Ref2
Modified script:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var values = sheet.getRange("C4:G45").getValues();
var numbers = values.map(function([c, d,,,g]) {return [
(c === true && d === true) ? g + 50 :
(c === true && d === false) ? g + 20 :
(c === false && d === true) ? g + 30 : g
]});
sheet.getRange(4, 16, numbers.length, 1).setValues(numbers);
}
References:
getValues()
setValues(values)
Benchmark: Reading and Writing Spreadsheet using Google Apps Script
Benchmark: Loop for Array Processing using Google Apps Script
Destructuring assignment
Related
I have a script that triggers on form submission, checks the row before last row and should write a TRUE value to the 9th column of the checked row IF all three cells on its left have a TRUE value.
My problem is that the script always writes a FALSE value, even if the 3 cells on the left are all TRUE.
function onFormSubmit() {
var s = SpreadsheetApp.getActiveSpreadsheet();
var rcore = s.getSheetByName("test");
var lastrow = rcore.getLastRow();
var trgt = rcore.getRange(lastrow-1,9);
if(trgt.getValue() === ""){
if(trgt.offset(0, -3) == "TRUE" && trgt.offset(0, -2) == "TRUE" && trgt.offset(0, -1) == "TRUE"){
trgt.setValue("TRUE");
} else {
trgt.setValue("FALSE");
}
}
}
(My language is set to hungarian so that's why you see "IGAZ" for "TRUE" values and "HAMIS" for "FALSE" values)
The 3 TRUE/FALSE values are generated by ARRAYFORMULA. Maybe that is also important
SO FAR
I have tried several variations:
-tried to change the if to check with offset (0, -4) if its equal to 2 and not check anything else, but still I got FALSE values.
-I also tried to check with different if statements but it always gives FALSE.
-tried to check what happens if I also offset row to -1
I simply cant get a TRUE value. The last time I was able to get a TRUE value was when there was no other if statement, only one that checks if the cell is empty or not.
As Rubén pointed out, you need to use .getValue() to read the value in a range. You should also use the Boolean values true and false instead of the text strings "TRUE" and "FALSE", like this:
function onFormSubmit() {
const ss = SpreadsheetApp.getActive();
const rcore = ss.getSheetByName('test');
const lastRow = rcore.getlastRow();
const trgt = rcore.getRange(lastRow - 1, 9);
if (trgt.getValue() === '') {
if (trgt.offset(0, -3).getValue() === true && trgt.offset(0, -2).getValue() === true && trgt.offset(0, -1).getValue() === true) {
trgt.setValue(true);
} else {
trgt.setValue(false);
}
}
}
...or more concisely:
function onFormSubmit() {
const rcore = SpreadsheetApp.getActive().getSheetByName('test');
const trgt = rcore.getRange(rcore.getlastRow() - 1, 9);
if (trgt.getValue() === '') {
trgt.setValue(trgt.offset(0, -3, 1, 3).getValues().flat().every(value => value === true));
}
}
I have a sheet where each row has a checkbox in the C column. I want to write my script such that if the checkbox in column C is unchecked, then columns G-K for that row will all be set to “N/A”.
I’ve seen things on here like getRange(“G2:K2”), but the row number is dynamic and I’m not sure how to do that.
I have it in an onEdit function and have the event row and column stored in variables.
Any help is appreciated. Thanks!
Could try a forEach loop. What is does is it reads each line of row col C to determine if its check and set n/a in the columns. So here in this case, it skips the row that is checked.
https://spreadsheet.dev/foreach-loop-in-apps-script
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var data=ss.getDataRange().getValues();
data.forEach(function(row,col){
if (col == '') return;
if (row[2] == true) return; //If colC is TRUE skip
data.getRange(col + 1, 7,1,5).setValue("n/a"); set colG - K as n/a if colC false
});
}
Description
Using the onEdit event object e you can get the range of the edited cell from that using an offset set the values of columns G to K
Script
function onEdit(e) {
if( e.range.getSheet().getName() === "Sheet1" ) { // Make sure we are on the right sheet
if( e.range.getColumn() === 3 ) { // Make sure the edit occured in column C
if( e.value === "FALSE" ) { // Unchecked
e.range.offset(0,4,1,5).setValues([["N/A","N/A","N/A","N/A","N/A"]]);
}
}
}
}
Reference
https://developers.google.com/apps-script/guides/triggers/events
https://developers.google.com/apps-script/reference/spreadsheet/range#offset(Integer,Integer,Integer,Integer)
Column A in my sheet has checkboxes. I'm writing a simple script to find the checkbox that is checked (cell value = TRUE), make it unchecked (change it to FALSE), and then check the next checkbox in the column (make that cell value = TRUE).
Here's my code:
function nextCheckbox() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var checkmarks = ss.getRangeByName("update_checkmarks").getValues().flat(); //this range is column A
Logger.log(checkmarks.indexOf(true)); //this logs 8.0, which is the correct row for the checked box in column A
var rowNum = checkmarks.indexOf(true);
Logger.log(rowNum); // this logs 8.0, as expected
var cell = sheet.getRange(rowNum,1);
cell.setValue(false); //nothing happens here...
var cell = sheet.getRange(rowNum + 1,1);
cell.setValue(true); //nothing happens here...
}
Logging logs the expected row number (8.0). But nothing happens when I use setValue. What am I doing incorrectly?
Regarding getting/setting values for checkboxes
Instead of using setValue consider to use check() / uncheck()
The above because checkboxes might use custom values for checked / unchecked states.
Regarding the use of indexOf / getRange
indexOf will return the index using 0 based numbering, this means 0 corresponds to the first value, 1 for the second and so on.
SpreadsheetApp.Sheet.Range.getRange(row,column) requires 1 based indexes, this means that the first row is 1, the second is 2.
Considering the above replace
var rowNum = checkmarks.indexOf(true);
by
var rowNum = checkmarks.indexOf(true) + 1;
Resources
https://developers.google.com/apps-script/reference/spreadsheet/range#check
https://developers.google.com/apps-script/reference/spreadsheet/range#uncheck
Your array starts at zero while your rows start at one. Currently your setting row 7 to be false(which it already is) and row 8 to be true, which it also already is.
Change this line in your code:
var rowNum = checkmarks.indexOf(true);
to be
var rowNum = checkmarks.indexOf(true)+1;
and you should get your expected results.
function onEdit(e) {
const sh = e.range.getSheet();
if(sh.getName() == 'Sheet1' && e.range.rowStart > 1 && e.range.rowStart < 12 && e.range.columnStart == 1 && e.value == "TRUE") {
e.range.setValue("FALSE");//reset
e.source.toast(e.range.getA1Notation());
}
}
Demo:
The entire script relies on two combinations of a For loop and an If statement. Both are essentially the same. But for some reason I can get the first section to work but not the second section.
I have included my script below with notes. Thank you.
/** #OnlyCurrentDoc */
function onEdit(e) {
//This part ensures the main script only runs if the cell F6 in the sheet "Daily Data" is edited.
if (
e.source.getSheetName() == "Daily Data" &&
e.range.columnStart == 6 &&
e.range.columnEnd == 6 &&
e.range.rowStart >= 6 &&
e.range.rowEnd <= 6
) {
//Secction 1: This section finds the lowest # from a list of numbers by finding the lowest empty cell in coloumn D using an If statement:
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var daily_data = spreadsheet.getSheetByName("Daily Data");
var entirelistoftimes = daily_data.getRange(3, 4, 62).getValues();
for(var i=0; i<entirelistoftimes.length ; i++){ //This For loop will run through the entire D column in sheet "Daily Data".
if (entirelistoftimes[i] == ""){ //This If statement will look for the first empty cell.
//Copies the Total Number:
var TotalNo = daily_data.getRange(i+2, 2).getValues(); //Gets the # from the cell next to the last filled cell in column D.
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Daily Data'), true);
spreadsheet.getRange('F8').setValues(TotalNo); //Displays the # in a cell F8 in sheet "Daily Data".
//Stop once the first blank cell has been found:
break;
}
}
//THIS IS THE SECTION I CANNOT GET TO WORK:
//Section 2: This section uses the # we got from the above section to find a time from a the corresponding row of sheet "Long Term Data":
var LTD_data = spreadsheet.getSheetByName("Long Term Data");
var LTD_data_entirelistoftimes = LTD_data.getRange(6, 2, 65).getValues();
for(var j=0; j<LTD_data_entirelistoftimes.length ; j++){ //This For loop will run through the Long Term Data sheet, through the list of numbers column.
if (LTD_data_entirelistoftimes[j] == TotalNo){ //This if statement will look through column B from row 6 for the # we got from section 1 above.
//Copies the time from column D in Lon:
var YesterdayTime = LTD_data.getRange(j, 4).getValues(); //Gets the time from column D in row j in the "Long Term Data" Sheet.
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Daily Data'), true);
spreadsheet.getRange('F9').setValues(YesterdayTime); //Displays the time from the time underneath the # in sheet "Daily Data".
//Stop once the above has been completed:
break;
}
}
}
}
;
If your script is modified, how about the following modification?
Modification point:
In your script, at if (LTD_data_entirelistoftimes[j] == TotalNo){, LTD_data_entirelistoftimes[j] and TotalNo are 1 dimensional array and 2 dimensional array, respectively. Because getValues() returns 2 dimensional array. I think that the reason of your issue is to directly compare those values.
Modified script:
Please modify your script as follows.
Pattern 1:
When == is used for the comparison operator, you can modify as follows.
From:
if (LTD_data_entirelistoftimes[j] == TotalNo){
To:
if (LTD_data_entirelistoftimes[j] == TotalNo[0][0]){
or
if (LTD_data_entirelistoftimes[j][0] == TotalNo[0][0]){
Pattern 2:
When === is used for the comparison operator, you can modify as follows.
From:
if (LTD_data_entirelistoftimes[j] == TotalNo){
To:
if (LTD_data_entirelistoftimes[j][0] === TotalNo[0][0]){
Note:
At if (entirelistoftimes[i] == ""){, == is used as the comparison operator. So the if statement works.
References:
getValues()
Comparison operators
If this modification didn't resolve your issue, I apologize. At that time, can you provide a sample Spreadsheet? By this, I would like to modify it.
I want to trigger a function when a cell changes from a vlookup function.
When I manually change a cell(column 2) value, below code works fine.
function onEdit(e)
{
var range = e.range;
var newValue = e.value;
var oldValue = e.oldValue;
var range = e.range;
if (range.getColumn() == 2){
Browser.msgBox('Old value : '+ e.oldValue +' - new value : '+ e.value);
}
}
But when I change it with vlookup cell function it doesn't trigger.
How can I achieve this?
A VLOOKUP formula will be recalculated when any of its paramenters is changed, so instead of range.getColumn() == 2 look for the edits done on the VLOOKUP precedents.
Let say that your vlookup formula is =VLOOKUP(D1,E:F,2,0), instead of range.getColumn() == 2 use range.rowStart === 1 && range.columnStart === 1 || range.columnStart === 5 || range.columnStart === 6.
Also you will have to change the Browser.msgBox parameter, instead of e.value you might will have to someway grab the VLOOKUP formula result. (there aren't enough details for being more specific). If you really need to the the previous result of VLOOKUP you will have to set a way to keep the old value, i.e., you could use the PropertiesService to keep the result of VLOOKUP, and on each edit first, grab the value, then update the corresponding property.