Delete rows after check text in a specific column - excel

I would like to know which script to use with the following conditions:
Built in trigger Google Sheets at a specific time, not after edit or change.
Delete entire rows where the text "canceled" is in one of the cells of column D (4).
Should only check in rows >=5.
Text "canceled" appears because of a formula in column D.
Specific sheets only!
What I have so far, but not working.
function deleterowoncheck (e){
var sheets = ["TEST"]; // Please set your expected sheet names;
var sheet = e.range.getSheet();
if (sheets.includes(sheet.getSheetName()))
if (e.range.getColumn() == 4 && e.range.getRow() >= 5 && e.range.getValue() == "CANCELED") {
{
var sheet = e.range.getSheet(); // Sheet in which the change was made
sheet.deleteRow(e.range.getRow());
e.source.toast('Deletion complete.');
}
}
}

You can try the following script:
function deleteRowOnCheck() {
var ss = SpreadsheetApp.getActive().getSheetByName("Name of the sheet");
var data = ss.createTextFinder("CANCELED").findAll();
for(var i=0; i<data.length; i++)
{
var textFinder = ss.createTextFinder("CANCELED");
textFinder.findNext();
ss.deleteRow(textFinder.getCurrentMatch().getRow());
}
}
Just remember to configure the installable time driven trigger as desired.
References:
Time-driven triggers
Class TextFinder

Related

How can I search for edited rows on Sheets to compare the values and change the timestamp on different columns accordingly?

I have been looking endlessly for a method to compare values on different columns in the same row, to know which cell I should update.
The speradsheet is a simple model of stock management (it's quite simple and I've been doing it manually), but I wanted a 'faster'(*) ou automated way of updating the amounts os each item, and the timestamps (which are two: one for adding units to the stock, and one for withdrawing).
The obstacles so far are:
The onEdit() function won't work on automated changes like macros, so it's off the table;
I need to scan the whole spreadsheet to find every filled cell on column D, which carries the value i'm adding to or subtracting from my column C;
-For this i have already setup do filter the column 'from Z to A' to get all the cells with values on them, but the amount of items changed can vary, so i cant set a search with a fixed number of rows.
Since my sheet has over 90 entries (likely to increase) of at least 6 columns each, a for loop with if statements takes too long, (*)but execution time is not exactly the main concern right now.
The code is as follows, and I'll be attaching a picture of the sheet I'm working with.
/** #OnlyCurrentDoc */
function geral() {
filtro();
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('G2').activate();
spreadsheet.getCurrentCell().setFormula('=C2+D2');
spreadsheet.getActiveRange().autoFill(spreadsheet.getRange('G2:G92'), SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);
var currentCell = spreadsheet.getCurrentCell();
spreadsheet.getSelection().getNextDataRange(SpreadsheetApp.Direction.DOWN).activate();
currentCell.activateAsCurrentCell();
spreadsheet.getRange('C2').activate();
spreadsheet.getRange('G2:G').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
spreadsheet.getRange('G:G').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
//adds the input OR output timestamp depending on the value in D column
//!!WORK IN PROGRESS!! --> here's where it gets tricky, and that's what I got so far (which doesn't work)
/*
for (var i = 2; i < 100; i++) {
spreadsheet.getRange('J2').setValue("TESTE");
var cell1 = spreadsheet.getRange('????').getValue(); //from this point on, I don't know how to proceed
var cell2 = spreadsheet.getRange('????').getValue();
spreadsheet.getRange('J2').setValue("TESTE2");
if(cell1 > cell2){
spreadsheet.getRange('????').activate();
spreadsheet.getActiveCell().setValue(new Date());
}
else if(cell1 < cell2){
spreadsheet.getRange('????').activate();
spreadsheet.getActiveCell().setValue(new Date());
}
}
*/
spreadsheet.getRange('D2:D').activate();
spreadsheet.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
};
function filtro() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('D:D').activate();
spreadsheet.getActiveSheet().sort(4, false);
};
EDIT: With my review after #IrvingJayG.'s comment, I noticed a few mistakes and unnecessary extra steps, so instead of doing all the copy-paste-delete dance and then compare results, I'd go for the pseudocode below:
//Ci's value pre-exists in the sheet, where i is the row index
//manually input Di value
//set formula for Gi = Ci+Di
//and then compare either Ci and Gi, or Di and 0
if(Di > 0){
//the following steps can be defined as a new function for each case, (e.g. updateIn() and updateOut())
copy Gi to Ci;
update Ei with new Date();
delete Gi and Di;
}
else if(Di < 0){
copy Gi to Ci;
update Fi with new Date();
delete Gi and Di;
}
Unfortunately, it still doesn't solve my problem, just simplifies the code by a lot.
Sheet example
RECOMMENDATION:
I've created a sample sheet (based on your attached example sheet) with 6 rows of data and with 4 random sample cell values on Column D. Then, I've created a sample script below, where you can use a reference:
NOTE: This script will scan every row on your sheet that has data (e.g. if you have 30 rows of data, it will scan every row one-by-one until it reaches the 30th row) and may slow-down once you have bunch of data on it. That's the catch because it's an expected behavior
SAMPLE SHEET:
SCRIPT:
function onOpen() { //[OPTIONAL] Created a custom menu "Timestamp" on your Spreadsheet, where you can run the script
var ui = SpreadsheetApp.getUi();
ui.createMenu('Timestamp')
.addItem('Automate Timestamp', 'mainFunction')
.addToUi();
}
function mainFunction() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('D:D').activate();
spreadsheet.getActiveSheet().sort(4, false);
automateSheetCheck();
}
function automateSheetCheck(){
var d = new Date();
var formattedDate = Utilities.formatDate(d, "GMT", "MM/dd/yyyy HH:mm:ss");
var spreadsheet = SpreadsheetApp.getActive();
var currentRow = spreadsheet.getDataRange().getLastRow(); //Get the last row with value on your sheet data as a whole to only scan rows with values
for(var x =2; x<=currentRow; x++){ //Loop starts at row 2
if(spreadsheet.getRange("D"+x).getValue() == ""){ //Checks if D (row# or x) value is null
Logger.log("Cell D"+x+" is empty"); //Logs the result for review
}else{
var res = spreadsheet.getRange("C"+x).getValue() + spreadsheet.getRange("D"+x).getValue(); //SUM of C & D values
if(spreadsheet.getRange("D"+x).getValue() > 0){ // If D value is greater than 0, E cell is updated with new timestamp and then C value is replaced with res
Logger.log("Updated Timestamp on cell E"+x + " because D"+x+ " with value of "+ spreadsheet.getRange("D"+x).getValue() +" is greater than 0"); //Logs the result for review
spreadsheet.getRange("E"+x).setValue(formattedDate);
spreadsheet.getRange("C"+x).setValue(res); //Replace C value with "res"
spreadsheet.getRange("D"+x).setValue(""); //remove D value
}else{ // If D value is less than 0, F cell is updated with a new timestamp
Logger.log("Updated Timestamp on cell F"+x + " because D"+x+ " with value of "+ spreadsheet.getRange("D"+x).getValue() +" is less than 0"); //Logs the result for review
spreadsheet.getRange("F"+x).setValue(formattedDate);
spreadsheet.getRange("C"+x).setValue(res); //Replace C value with "res"
spreadsheet.getRange("D"+x).setValue(""); //remove D value
}
}
}
}
RESULT:
After running the script, the will be the result on the sample sheet:
Here's the Execution Logs, where that you can review what happened after running the code:

Delete rows after a date has passed automatically for Google Spreadsheets [duplicate]

I'd like to be able to delete an entire row in a Google Spreadsheets if the value entered for say column "C" in that row is 0 or blank. Is there a simple script I could write to accomplish this?
Thanks!
I can suggest a simple solution without using a script !!
Lets say you want to delete rows with empty text in column C.
Sort the data (Data Menu -> Sort sheet by column C, A->Z) in the sheet w.r.t column C, so all your empty text rows will be available together.
Just select those rows all together and right-click -> delete rows.
Then you can re-sort your data according to the column you need.
Done.
function onEdit(e) {
//Logger.log(JSON.stringify(e));
//{"source":{},"range":{"rowStart":1,"rowEnd":1,"columnEnd":1,"columnStart":1},"value":"1","user":{"email":"","nickname":""},"authMode":{}}
try {
var ss = e.source; // Just pull the spreadsheet object from the one already being passed to onEdit
var s = ss.getActiveSheet();
// Conditions are by sheet and a single cell in a certain column
if (s.getName() == 'Sheet1' && // change to your own
e.range.columnStart == 3 && e.range.columnEnd == 3 && // only look at edits happening in col C which is 3
e.range.rowStart == e.range.rowEnd ) { // only look at single row edits which will equal a single cell
checkCellValue(e);
}
} catch (error) { Logger.log(error); }
};
function checkCellValue(e) {
if ( !e.value || e.value == 0) { // Delete if value is zero or empty
e.source.getActiveSheet().deleteRow(e.range.rowStart);
}
}
This only looks at the value from a single cell edit now and not the values in the whole sheet.
I wrote this script to do the same thing for one of my Google spreadsheets. I wanted to be able to run the script after all the data was in the spreadsheet so I have the script adding a menu option to run the script.
/**
* Deletes rows in the active spreadsheet that contain 0 or
* a blank valuein column "C".
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[2] == 0 || row[2] == '') {
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
}
};
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the readRows() function specified above.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Remove rows where column C is 0 or blank",
functionName : "readRows"
}];
sheet.addMenu("Script Center Menu", entries);
};
Test spreadsheet before:
Running script from menu:
After running script:
I was having a few problems with scripts so my workaround was to use the "Filter" tool.
Select all spreadsheet data
Click filter tool icon (looks like wine glass)
Click the newly available filter icon in the first cell of the column you wish to search.
Select "Filter By Condition" > Set the conditions (I was using "Text Contains" > "word")
This will leave the rows that contain the word your searching for and they can be deleted by bulk selecting them while holding the shift key > right click > delete rows.
This is what I managed to make work. You can see that I looped backwards through the sheet so that as a row was deleted the next row wouldn't be skipped. I hope this helps somebody.
function UpdateLog() {
var returnSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('RetLog');
var rowCount = returnSheet.getLastRow();
for (i = rowCount; i > 0; i--) {
var rrCell = 'G' + i;
var cell = returnSheet.getRange(rrCell).getValue();
if (cell > 0 ){
logSheet.
returnSheet.deleteRow(i);
}
}
}
quite simple request. Try this :
function try_It(){
deleteRow(2); //// choose col = 2 for column C
}
function deleteRow(col){ // col is the index of the column to check for 0 or empty
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n][col]!='' && data[n][col]!=0){ targetData.push(data[n])};
}
Logger.log(targetData);
sh.getDataRange().clear();
sh.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
}
EDIT : re-reading the question I'm not sure if the question is asking for a 'live' on Edit function or a function (like this above) to apply after data has been entered... It's not very clear to me... so feel free to be more accurate if necessary ;)
There is a simpler way:
Use filtering to only show the rows which you want to delete. For example, my column based on which I want to delete rows had categories on them, A, B, C. Through the filtering interface I selected only A and B, which I wanted to delete.
Select all rows and delete them. Doing this, in my example, effectively selected all A and B rows and deleted them; now my spreadsheet does not show any rows.
Turn off the filter. This unhides my C rows. Done!
There is a short way to solve that instead of a script.
Select entire data > Go to menu > click Data tab > select create filter > click on filter next to column header > pop-up will appear then check values you want to delete > click okay and copy the filtered data to a different sheet > FINISH
reading your question carefully, I came up with this solution:
function onOpen() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// create menu
var menu = [{name: "Evaluate Column C", functionName: "deleteRow"}];
// add to menu
ss.addMenu("Check", menu);
}
function deleteRow() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get active/selected row
var activeRow = ss.getActiveRange().getRowIndex();
// get content column C
var columnC = ss.getRange("C"+activeRow).getValue();
// evaluate whether content is blank or 0 (null)
if (columnC == '' || columnC == 0) {
ss.deleteRow(parseInt(activeRow));
}
}
This script will create a menu upon file load and will enable you to delete a row, based on those criteria set in column C, or not.
This simple code did the job for me!
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); // get active spreadsheet
var activeRow = ss.getActiveRange().getRowIndex(); // get active/selected row
var start=1;
var end=650;
var match='';
var match2=0; //Edit this according to your choice.
for (var i = start; i <= end; i++) {
var columnC = ss.getRange("C"+i).getValue();
if (columnC ==match || columnC ==match2){ ss.deleteRow(i); }
}
}
The below code was able to delete rows containing a date more than 50 days before today in a particular column G , move these row values to back up sheet and delete the rows from source sheet.
The code is better as it deletes the rows at one go rather than deleting one by one. Runs much faster.
It does not copy back values like some solutions suggested (by pushing into an array and copying back to sheet). If I follow that logic, I am losing formulas contained in these cells.
I run the function everyday in the night (scheduled) when no one is using the sheet.
function delete_old(){
//delete > 50 day old records and copy to backup
//run daily from owner login
var ss = SpreadsheetApp.getActiveSpreadsheet();
var bill = ss.getSheetByName("Allotted");
var backss = SpreadsheetApp.openById("..."); //backup spreadsheet
var bill2 = backss.getSheetByName("Allotted");
var today=new Date();
//process allotted sheet (bills)
bill.getRange(1, 1, bill.getMaxRows(), bill.getMaxColumns()).activate();
ss.getActiveRange().offset(1, 0, ss.getActiveRange().getNumRows() - 1).sort({column: 7, ascending: true});
var data = bill.getDataRange().getValues();
var delData = new Array();
for(n=data.length-1; n>1; n--){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){ //change the condition as per your situation
delData.push(data[n]);
}//if
}//for
//get first and last row no to be deleted
for(n=1;n<data.length; n++){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){
var strow=n+1 ; //first row
break
}//if
}//for
for(n=data.length-1; n>1; n--){
if(data[n][6] !=="" && data[n][6] < today.getTime()-(50*24*3600*1000) ){
var ltrow=n+1 ; //last row
break
}//if
}//for
var bill2lr=bill2.getLastRow();
bill2.getRange((bill2lr+1),1,delData.length,delData[0].length).setValues(delData);
bill.deleteRows(strow, 1+ltrow-strow);
bill.getRange(1, 1, bill.getMaxRows(), bill.getMaxColumns()).activate();
ss.getActiveRange().offset(1, 0, ss.getActiveRange().getNumRows() - 1).sort({column: 6, ascending: true}); //get back ordinal sorting order as per column F
}//function

Find value on any sheet in spreadsheets using Google Script

Using the code below I'm able to look through multiple sheets in a spreadsheet to find the first value that equals the selected cell. The only problem with this bit is: The cell with the value found is highlighted yellow, but the cell with the value found isn't selected. See code below for hopping through sheets. I can't get my head around this :)
Funny thing is that the code for highlighting and selecting a value does work when I'm not hopping through the list of sheets, see the best answer: Find value in spreadsheet using google script
function SearchAndFind() {
//determine value of selected cell
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var cell = ss.getActiveCell();
var value = cell.getValue();
//create array with sheets in active spreadsheet
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
//loop through sheets to look for value
for (var i in sheets) {
//Set active cell to A1 on each sheet to start looking from there
SpreadsheetApp.setActiveSheet(sheets[i])
var sheet = sh.getActiveSheet();
var range = sheet.getRange("A1");
sheet.setActiveRange(range);
//set variables to loop through data on each sheet
var activeR = cell.getRow()-1;
var activeC = cell.getColumn()-1;
var data = sheets[i].getDataRange().getValues()
var step = 0
//loop through data on the sheet
for(var r=activeR;r<data.length;++r){
for(var c=activeC;c<data[0].length;++c){
step++
Logger.log(step+' -- '+value+' = '+data[r][c]);
if(data[r][c]==''||step==1){ continue };
if(value.toString().toLowerCase()==data[r][c].toString().toLowerCase()){
sheet.getRange(r+1,c+1).activate().setBackground('#ffff55');
return;
}
}
}
}
}
This code is able to search across multiple sheets, it is obviously based on your published code but uses a memory (scriptProperties) to keep the search value 'alive' when changing from one sheet to the next one and to know when to search for it.
It has 2 non-optimal aspects : 1° you have to keep searching up to the last occurrence before you can begin a new search.
2° : when it switches from sheet n to sheet n+1 it first selects cell A1 before finding the value occurrence.
I guess it should be possible to get rid of these issues but right now I don't find how :-)
Maybe the approach is simply not the best, I started from a simple one sheet script modified and complexified... that's usually not the best development strategy (I know), but anyway, it was a funny experiment and a good logic exercise ...
Thanks for that.
function SearchAndFind() {
//determine value of selected cell
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var cell = ss.getActiveCell();
var value = cell.getValue();
if(ScriptProperties.getProperty('valueToFind')!=''){value = ScriptProperties.getProperty('valueToFind')};
//create array with sheets in active spreadsheet
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets()
var sheetNumber = sheets.length;
var currentSheet = ss.getIndex()-1;
Logger.log(currentSheet);
//loop through sheets to look for value
for (var i = currentSheet ; i<sheetNumber ; ++i ){
Logger.log('currentSheet = '+i)
//Set active cell to A1 on each sheet to start looking from there
SpreadsheetApp.setActiveSheet(sheets[i])
// sheets[i].getRange(1,1).activate();
//set variables to loop through data on each sheet
var activeR = cell.getRow()-1;
var activeC = cell.getColumn()-1;
var data = sheets[i].getDataRange().getValues()
var step = 0;
//loop through data on sheet
for(var r=activeR;r<data.length;++r){
for(var c=activeC;c<data[0].length;++c){
step++
Logger.log('sheet : '+i+' step:'+step+' value '+value+' = '+data[r][c]);
if(data[r][c]==''||(step==1&&i==currentSheet)){ continue };
if(value.toString().toLowerCase()==data[r][c].toString().toLowerCase()){
sheets[i].getRange(r+1,c+1).activate().setBackground('#ffff55');
ScriptProperties.setProperty('valueToFind',value);
return;
}
}
}
cell = sheets[i].getRange(1,1);
}
ScriptProperties.setProperty('valueToFind','');
Logger.log('reset');
}

How to make Google form submission sort into separate sheets?

I can only get this to sort automatically when I input the text manually into the cell. I've tried changing to OnFormSubmit but no luck. How can I rewrite this to have Google Docs automatically sort the form-submitted answers to separate tabs?
function onEdit(event) {
// assumes source data in sheet named Needed
// target sheet of move to named Acquired
// test column with yes/no is col 4 or D
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = SpreadsheetApp.getActiveSheet();
var r = SpreadsheetApp.getActiveRange();
if(s.getName() == "Inbox" && r.getColumn() == 6 && r.getValue() == "Los Angeles") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Los Angeles");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row)
}
}
Dont write the sorted sheet data manually. Instead use a single QUERY formula per sheet. With it you can filter sort grouo and pivot your data as you like.

Get and use user data from another sheet in Google Docs

We use google spreadsheets for reporting by quite a big number of users.
I have written a basic script, which opens a specific sheet depending on the current user:
var CurrentUser = Session.getUser().getEmail()
var ss = SpreadsheetApp.getActive();
switch(CurrentUser){
case "usermail1#gmail.com":
SpreadsheetApp.setActiveSheet(ss.getSheetByName("sheet1"));
break;
case "usermail2#gmail.com":
SpreadsheetApp.setActiveSheet(ss.getSheetByName("sheet2"));
break;
case "usermail3#gmail.com":
SpreadsheetApp.setActiveSheet(ss.getSheetByName("sheet3"));
break;
etc...
I would like to put the userdata and sheetnames into an external table and get these data depending on that table, so it is easier to maintain the list of e-mails and users.
How can I get data from a specific google spreadsheet and let the script work according to that?
You can try this. It simulates a VLOOKUP on a different sheet and switches to the 'matched' sheet in your current workbook. This doesn't handle non-matches, but that should be relatively straightforward to add to suit your case.
function switchSheets() {
// Current sheet
var ss = SpreadsheetApp.getActive();
// Target sheet (using the key found in the URL)
var target = SpreadsheetApp.openById("my_other_sheet_id");
var rows = target.getDataRange();
var values = rows.getValues();
// Get the current user
var CurrentUser = Session.getUser().getEmail();
// Now iterate through all of the rows in your target sheet,
// looking for a row that matches your current user (this assumes that
// it will find a match - you'll want to handle non-matches as well)
for (var i = 1; i <= rows.getNumRows() - 1; i++) {
// Match the leftmost column
var row = values[i][0];
if (row == CurrentUser) {
// If we match, grab the corresponding sheet (one column to the right)
target_sheet = values[i][1];
break;
}
}
// Now switch to the matched sheet (rememeber to handle non-matches)
ss.setActiveSheet(ss.getSheetByName(target_sheet));
};

Resources