I am trying to clear some values in a defined named range taking into account also other value from another defined range of the same size. Here is my code below.
I get a Out of range on rng_stages.getCell(i,0).clearContent()
I suspect I no longer have a pointer to the sheet itself. As you can see I am not familiar with cell value assignment.
Does onOpen only triggers when the workbook opens or it also triggers each time a sheet is open in the same workbook? The named ranges only exist in the first sheet.
Here is the code:
function onOpen() {
var rng_stages = SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ClaimStages');
var rng_levels= SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ClaimLevels');
var arr_stages = rng_stages.getValues();
var arr_levels = rng_levels.getValues();
for (var i = 0; i < arr_stages.length; i++) {
if ((arr_stages[i][0] == 'Approved') && (arr_levels[i][0] == -1)) {
rng_stages.getCell(i,0).clearContent()
}
}
}
function myFunction() {
const ss = SpreadsheetApp.getActive();
const rng_stages = ss.getRangeByName('ClaimStages');
const rng_levels = ss.getRangeByName('ClaimLevels');
const arr_stages = rng_stages.getValues();
const arr_levels = rng_levels.getValues();
for (let i = 0; i < arr_stages.length; i++) {
if ((arr_stages[i][0] == 'Approved') && (arr_levels[i][0] == -1)) {
rng_stages.getCell(i+1, 1).clearContent()
}
}
}
Range.getCell(row, column)
Rows and Columns begin at 1
Related
This is the script I have currently. I just picked up script editing recently and I cant resolve this issue.
I am trying to delete the same row in 2 sheets.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Verification');
var r = s.getRange('Q:Q');
var v = r.getValues();
for(var i=v.length-1;i>=0;i--)
if(v[0,i] == 'Cancelled')
s.deleteRow(i+1);
ss.getSheetByName('S4 Branch');
s.deleteRow(i+1);
I believe your goal as follows.
You want to check the values of the column "Q" of "Verification" sheet.
When the value is Cancelled, you want to delete the row, and also, you want to delete the row of the same row number in "S4 Branch" sheet.
Modification points:
If you want to compare the value of Cancelled with v, please modify if(v[0,i] == 'Cancelled') to if(v[i][0] == 'Cancelled').
When var r = s.getRange('Q1:Q' + s.getLastRow()); is used as the data range instead of var r = s.getRange('Q:Q');, the process cost will be able to be reduced a little.
In your script, the if statement and the for loop are not enclosed by { and }. By this, each script is used for just under the line. For example, for(var i=v.length-1;i>=0;i--) is used for if(v[0,i] == 'Cancelled'). And, if(v[0,i] == 'Cancelled') is used for s.deleteRow(i+1);. By this, i at s.deleteRow(i+1); is always -1. So s.deleteRow(0); is run. In this case, an error occurs. I think that this might be the reason of your issue.
When above points are reflected to your script, it becomes as follows.
Modified script:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Verification');
var r = s.getRange('Q1:Q' + s.getLastRow());
var v = r.getValues();
var s2 = ss.getSheetByName("S4 Branch");
for (var i = v.length - 1; i >= 0; i--) {
if (v[i][0] == 'Cancelled') {
s.deleteRow(i + 1);
s2.deleteRow(i + 1);
}
}
}
References:
getLastRow()
deleteRow(rowPosition)
If you want to try the below script:
You can add more sheets by updating the sheetArr list and it can auto-trigger on Edit.
function onEdit() {
deleteRow();
}
function deleteRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetArr = ['Verification', 'S4 Branch'];
for (var i = 0; i<sheetArr.length; i++){
var lr = ss.getSheetByName(sheetArr[i]).getDataRange().getLastRow();
var s = ss.getSheetByName(sheetArr[i]).getRange('Q1:Q' + lr ).getValues();
for (var j = s.length -1; j >= 0; j--){
if (s[j] == 'Cancelled'){
delrow = ss.getSheetByName(sheetArr[i]).deleteRow(j+1);
}
}
}
}
I am trying to calculate some values using my own function (showing dummy function in this question), and I'd like the function to trigger when the user edits a cell in a given range, however even after writing the trigger the only time when the value is calculated again is when I go to the script editor and click Save.
My function:
function calculate(column) {
var sheet = SpreadsheetApp.getActiveSheet();
var column = sheet.getActiveCell().getColumn();
var values2d = sheet.getRange(1, 1, 15).getValues();
var values = [].concat.apply([], values2d);
var testRange2d = sheet.getRange(1, column, 15).getValues();
var testRange = [].concat.apply([], testRange2d);
var sum = 0;
for (var i = 0; i < 15; i++) {
if (testRange[i] == true) sum += values[i]
}
return sum;
}
function onEdit(e) {
var activeCell = SpreadsheetApp.getActiveSheet().getActiveCell();
if (activeCell.getBackground() === "#e3f5a2") activeCell.setBackground("#ffffff");
else activeCell.setBackground("#e3f5a2");
calculate();
}
And the sheet:
The cell besides total is where I call my function. And the value will only be updated either manually or as described earlier, but even when I trigger an event, I have also tried doing other actions in the trigger like changing a random cells value and that worked however.
Might i suggest the following. This will only occur if a checkbox in column 2 is changed.
function onEdit(event) {
try {
var sheet = event.source.getSheetByName("Sheet1")
if( event.range.getSheet().getName() === "Sheet1" ) {
if( event.range.getColumn() === 2 ) {
var values = sheet.getRange(1,1,15,2).getValues();
var sum = 0;
for( var i=0; i<values.length; i++ ) {
if( values[i][1] ) sum += values[i][0];
}
sheet.getRange(16,2,1,1).setValue(sum);
if( event.range.getBackground() === "#e3f5a2" )
event.range.setBackground("#ffffff");
else
event.range.setBackground("#e3f5a2");
}
}
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}
I am trying to achieve a script that will delete a row based upon a string in a cell within that row,
I can achieve my mission if I want to name each individual sheet, however my sheets are constantly changing names, so this would mean updating the the script daily.
I have tried to create a loop, but that does not seem to work either, any suggestions?
The script I am working on is below.
function RemoveInvoiced() {
var ss = SpreadsheetApp.openById("1pxWr3jOYZbcwlFR7igpSWa9BKCa2tTeliE8FwFCRTcQ").getSheets();
for (var k=0; k<ss.length; k++)
var values = ss[k].getDataRange().getValues();
var rows_deleted = 0;
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
var value = values[i][j];
var row = i + 1 - rows_deleted;
if (typeof value === 'string') {
var result = value.search("INVOICED");
if (result !== -1) {
ss.deleteRow(row);
rows_deleted++;
}
}
}
}
};
Instead of using the sheet name to access them, you could access them by Id.
The id will never change as long as you don't delete the sheet, even if you change the name.
You'd first have to get the sheets Id, a simple function could do that for you
function getId(sheet){
Logger.log(sheet.getSheetId());
}
Once you have gotten your sheet ids, you can make a function that will open the right sheet by the ID
function openSheetById(doc, id){
var sheets = doc.getSheets();
for(var i = 0; i< sheets.length ; i++){
if( sheets[i].getSheetId() == id)
return sheets[i];
}
return false;
}
And then you can access the right sheet.
function myFunc(){
var doc = SpreadsheetApp.openById("1pxWr3jOYZbcwlFR7igpSWa9BKCa2tTeliE8FwFCRTcQ");
var id = // your id here
var sheet = openSheetById(doc, id);
if(sheet){
//do other stuffs
}
}
My spreadsheet is composed of a main sheet that is populated using a form plus several other sheets for the people who work with the responses submitted through the form. A script delegates the form responses to these other sheets depending on the type of item described in the response.
The problem is, when Person A deletes an item from their respective sheet, it doesn't delete in the main sheet.
My idea is that when you type a set password into the corresponding cell in row 'Q' in Person A's sheet, it matches the item by timestamp to the original form submission and deletes both the version of the item in Person A's sheet as well as the main sheet. However, I can't figure out what to set the range to to get it to point to the row in the array. Everything I have tried has sent back "undefined" in the debugger and won't delete anything. I think the problem is that I don't know how to get the row from the array that I have made. See my code below:
function onEdit() {//copies edited items from individual selector sheets back onto main spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actSheet = ss.getActiveSheet();
var responseSheet = ss.getSheetByName("Item Request");
var actCell = actSheet.getActiveCell();
var actRow = actCell.getRow();
var actVal = actCell.getValue();
var actLoc = actCell.getA1Notation();
var last = actSheet.getLastRow();
var respLast = responseSheet.getLastRow();
var dataA = responseSheet.getRange(1, 1, respLast, 1).getValues(); //compiles an array of data found in column A through last row in response sheet
var tstamp1 = actSheet.getRange(actCell.getRow(), 1);
var tsVal1 = tstamp1.getValue();
var colEdit = actCell.getColumn();
//===========THIS IS WHERE I'M STUCK=======================
if ((actVal == "p#ssword") && (colEdit == 17)) {
for (i = 1; i < dataA.length; i++) {
if (dataA[i][0].toString == tsVal1.toString()) {
responseSheet.deleteRow(i + 1);
actSheet.deleteRow(actRow);
break;
}
}
}
else if (colEdit == 15) { //checks the array to see if the edit was made to the "O" column
for (i = 1; i < dataA.length; i++) {//checking for timestamp match and copies entry
if (dataA[i][0].toString() == tsVal1.toString()) {
var toEdit = responseSheet.getRange(i + 1, 16);
toEdit.setValue(actVal);
}
}
}
else if (colEdit == 16) { // checks the array to see if the edit was made in the "P" column
for (i = 1; i < dataA.length; i++) {//checking for timestamp match and copies entry
if (dataA[i][0].toString() == tsVal1.toString()) {
var toEdit = responseSheet.getRange(i + 1, 17);
toEdit.setValue(actVal);
}
}
}
else {return;}
}//end onEdit
I don't believe these are proper commands delRow.deleteRow();actCell.deleteRow(); Take a look at the documentation;
Okay I rewrote that function for you a bit but I'm stilling wondering about a couple of lines.
function onEdit(e)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actSheet = ss.getActiveSheet();
var responseSheet = ss.getSheetByName("Item Request");
var actCell = actSheet.getActiveCell();
var actRow = actCell.getRow();
var actVal = actCell.getValue();
var colEdit = actCell.getColumn();
var respLast = responseSheet.getLastRow();
var dataA = responseSheet.getRange(1, 1, respLast, 1).getValues();
var tstamp1 = actSheet.getRange(actRow, 1);
var tsVal1 = tstamp1.getValue();
for(var i=0;i<dataA.length;i++)
{
if(new Date(dataA[i][0]).valueOf()==new Date(tsVal1).valueOf())
{
if (actVal=="p#ssword" && colEdit==17)
{
responseSheet.deleteRow(i + 1);
actSheet.deleteRow(actRow);
}
else if(colEdit==15)
{
var toEdit = responseSheet.getRange(i + 1, 16);//?
toEdit.setValue(actVal);//?
}
else if (colEdit == 16)
{
var toEdit = responseSheet.getRange(i + 1, 17);//?
toEdit.setValue(actVal);//?
}
}
}
}
Can you explain the function of the lines with question marked comments?
I have a Spreadsheet with some functions. One of them is a onEdit(event) function that copies some values to other sheets based on conditions. This is the code (simplified but with the important parts intact):
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.range;
if(s.getName() === "Lista" && r.getColumn() === 9 && r.getValue() === "Posicionada") {
var sheetname = s.getRange(r.getRow(),3).getValue();
var columnRef = s.getRange(r.getRow(),4).getValue();
var row = s.getRange(r.getRow(),5).getValue();
var targetSheet = ss.getSheetByName("Mapa " + sheetname);
var headers = targetSheet.getRange(1, 1, 1, targetSheet.getLastColumn());
for (var i = 0; i < headers; i++) {
if (headers[i] === columnRef) {
break;
}
}
var column;
if (columnRef === "A1") {
column = 2;
}
else if (columnRef === "A2") {
column = 3;
}
else if (columnRef === "B1") {
column = 4;
}
else if (columnRef === "B2") {
column = 5;
}
if (sheetname === "N2") {
row = row - 30;
}
if (sheetname === "N3") {
column = column - 10;
row = row - 42;
}
targetSheet.getRange(row,column).setValue(s.getRange(r.getRow(), 1, 1, 1).getValue());
}
}
The code works as it should when I manually edit the cell. But, I have a code that edit the cell when the user press a button in a sidebar, this is the code:
function positionMU(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var cell = ss.getActiveCell().activate();
var cellLevel = cell.offset(0,2);
var cellLetter = cell.offset(0,3);
var cellNumber = cell.offset(0,4);
var cellStatus = cell.offset(0,8);
var dbq = "Posicionada";
var fora = "Pendente de recebimento";
if (cellStatus.getValue() == "Aguardando posicionamento"){
cellStatus.setValue(dbq); //attention in this line
}
else if (cellStatus.getValue() == "Aguardando saĆda"){
cellStatus.setValue(fora);
var cellExitDate = cell.offset(0,6);
cellExitDate.setValue(getDate());
}
}
As you can see, this function change the cell content with setValue(), but, when I use this function, the value of the cell changes, but the onEdit() trigger doesn't work.
How can I make the onEdit() trigger recognize changes made with setValue()?
You are right. onEdit() only triggers if the range is edited manually. As can be seen here, onEdit() triggers when a value is changed by the user.
I tested the function by making function to insert values into a column for which my onEdit responds and nothing happens. Including various other techniques that I could think of. Best thing to do here is to suggest this as an enhancement on App Script's Issue Tracker.
However, I made it work by writing another function to be called when another function in the script makes changes to the sheet. These are the test functions I wrote:
function addValues()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getDataRange();
var book = "Book";
var cancel = "Cancel";
var maxRow = range.getLastRow()+1;
for(var i=0; i<4; i++)
{
if (i%2 == 0)
{
sheet.getRange(maxRow, 1).setValue(book);
autoChanges(maxRow);
}else{
sheet.getRange(maxRow, 1).setValue(cancel);
autoChanges(maxRow);
}
maxRow++;
}
}
autoChanges function:
function autoChanges(row)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getDataRange();
var data = range.getValues();
var response = "";
sheet.getRange(row, 2).protect();
response = data[row-1][0];
if (response == "Book")
{
sheet.getRange(row, 2).canEdit();
}else{
sheet.getRange(row, 2).setValue("--NA--");
}
}
Not the most elegant solution but this seems to be the only workaround for what you are trying to do.
There are some very good reasons why calling range.setValue() doesn't trigger the onEdit event, and most of them have to do with infinite recursion. In fact, you call setValue() yourself WITHIN onEdit(). This would trigger a recursive call, and from what I can see, you have no provision for handling the base case and thus your code would explode if setValue() did what you want.
Why not simply take all of your code out of your event handler, and put it into another function:
function onEdit (e) {
return handleEdits(e.range);
}
function handleEdits(r) {
s = r.getSheet();
ss = s.getParent();
//the rest of your code should drop right in.
}
then, inside your autoChanges function, go ahead and call handleEdits, passing it an appropriate range, after your call to setValue().
If you like to play with fire, and I personally do, you can call the onEdit(e) function after you make a change. Just send in an object whatever is called and used by the e object.
For me, I just needed to add:
var e={};
e.range=the range you are making a change to in the script;
e.source = SpreadsheetApp.getActiveSpreadsheet();//or make sure you have the sheet for wherever you are setting the value
e.value=whatever value you are setting
e.oldValue=if you onEdit needs this, set it here
//if you are using any of the other standard or special properties called by your onEdit...just add them before calling the function.
onEdit(e);//call the function and give it what it needs.