Move Rows based on matches between 3 columns - google-apps-script

My goal is to compare the entries', from the Data tab, First Name, Last Name, and Date of Birth to the same columns on another tab named Completed and erase the matching rows from the Data tab.
Marking a checkbox on another tab New Cases should find the row on Data, Move that row to Completed, and clear the checkbox on New Cases.
https://docs.google.com/spreadsheets/d/1sVVzW4Ga6XzYrGS-7AqwdMCqeL1-IxzRVSHG2FxMCog/edit?usp=sharing
I found a couple scripts that approached what I wanted, and I copied them into the sheet, but I don't have the expertise to get either (Move row based on cell selection) or (https://support.google.com/docs/thread/39992635?msgid=40432488) to accomplish the solution.
I was able to get something like this to work, but it only worked once, and I was only able to get it to move a row from (New Cases) to (Completed).
function onEdit(event) {
// assumes source data in sheet named New Cases
// target sheet of move to named Completed
// getColumn with check-boxes is currently set to colu 21 or V
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "New Cases" && r.getColumn() == 21 && r.getValue() == true) {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Completed");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
Edit: I'm trying to get any row from "Data" where Columns C, D, AND E match any row on "Completed" to automatically delete off of the "Data" sheet.
Then, when the checkbox on "New Cases" is checked in column V, it moves the matching row from "Data" to "Completed" and unchecks the box on "New Cases" so it's ready for the next entry.

Select checkbox on New Cases Find row on Data move Data row to Completed
Delete row on Data and reset checkbox on new cases.
function onEdit(e) {
const nsh = e.range.getSheet();
if (nsh.getName() == "New Cases" && e.range.columnStart == 22 && e.range.rowStart > 1 && e.value() == "TRUE") {
const nrg = nsh.getRange(e.range.rowStart, 1, 1, nsh.getLastColumn());
const nvs = nrg.getValues().flat();//row
const dsh = e.source.getSheetByName("Data");
const dvs = dsh.getRange(2, 1, dsh.getLastRow() - 1, dsh.getLastColumn()).getValues();
const csh = e.source.getSheetByName("Completed");
const tgt = csh.getRange(csh.getLastRow() + 1, 1);
for (let i = 0; i < dvs.length; i++) {
if (dvs[i][2] == nvs[2] && dvs[i][3] == nvs[3] && dvs[i][4] == nvs[4]) {
let drg = dsh.getRange(i + 1, 1, 1, dsh.getLastColumn());
drg.copyTo(tgt);
dsh.deleteRow(i + 1);
break;
}
}
e.range.setValue("FALSE");//reset the checkbox
//e.range.offset(0,1).setValue(new Date());//you should consider doing this so that you know that it has been moved. The way it setup now it would put the timestamp in column W
}
}

Related

How can I move a column to a new sheet once row 1 has a certain value?

I am using the code posted here that moves a row based on the value of a column, but I want to edit it so that it does the opposite: moves a column to a new sheet based on the value of a row.
This is what I have so far:
function onEdit(event) {
// source data in sheet named Blocks
// sheet column is moved to named Ordered
// test row with word "Done" is row 1
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "Blocks" && r.getRow() == 1 && r.getValue() == "Done") {
var column = r.getColumn();
var numRows = s.getLastRow();
var targetSheet = ss.getSheetByName("Ordered");
var target = targetSheet.getRange(targetSheet.getLastColumn() + 1, 1);
s.getRange(column, 1, 1, numRows).copyTo(target);
s.deleteColumn(column);
}
}
The problem is, when I tested it, it only moved the first row of that column. I want it to move the entire column and then delete it from the original sheet.
Can anyone tell me where I went wrong?
Moving Selected Columns
function onEdit(e) {
//e.source.toast('Entry');
var sh = e.range.getSheet();
if(sh.getName() == "Blocks" && e.range.rowStart == 1 && e.value == "Done") {
//e.source.toast('Ordered1')
var tsh = e.source.getSheetByName("Ordered");
var target = tsh.getRange(1,tsh.getLastColumn() + 1);
sh.getRange(2, e.range.columnStart, sh.getLastRow() -1).copyTo(target);
sh.deleteColumn(e.range.columnStart);
}
}
I used Active/Done datavalidation on row one and I only moved from row two to last row and then deleted the entire column including the validated cell.

Move row based on cell selection

I have a script that moves the whole row to a different tab and deletes it from the tab it was moved based on a "list" selection.
The script is the following:
// Script Move & Delete
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 = event.source.getActiveSheet();
var r = event.source.getActiveRange();
// In Review Trigger
if(s.getName() == "Applied" && r.getColumn() == 1 && r.getValue() == "In Review") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("In Review");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
// In Review Trigger
else if(s.getName() == "Applied" && r.getColumn() == 1 && r.getValue() == "Failed") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Failed");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
So for example, if the row on Tab "Applied" and value of cell on column 1 = In Review then the row is moved to tab "In Review" but if the value of cell on column one = to Failed then row is moved to tab Failed.
That part works like charm and I know having multiple tabs will have a lot of else ifs, I'm not much of a dev, so I was wondering how can I do to move the row if the status (Column 1) is changed to Applied is moved to tab one or failed tab if value is changed to failed?
Basically, move the rows between tabs based on value of column one of each row/tab.
Thanks in advance!
This worked for me:
function onMyEdit(e) {
//e.source.toast('entry');
const s = e.range.getSheet();
if (s.getName() == "Sheet1" && e.range.columnStart == 1 && e.value) {
//e.source.toast('cond')
const names = ['Sheet2', 'Sheet3'];
let idx = names.indexOf(e.value);
if (~idx) {
//e.source.toast('sheet');
let tsh = e.source.getSheetByName(names[idx]);
let tgt = tsh.getRange(tsh.getLastRow() + 1, 1);
s.getRange(e.range.rowStart, 1, 1, s.getLastColumn()).moveTo(tgt);
s.deleteRow(e.range.rowStart);
}
}
}
I made some name changes to make it easier for me to debug so look it over to make sure all of the sheet names are right for your needs.
If you require more complex functionality in moving rows, take a look at the moveRowsFromSpreadsheetToSpreadsheet_ script. It is unfortunately too long to quote here directly.

Moving data from one row to another in the same sheet with a check box

I currently have a row with dropdown lists and I want to move the data in this row to a separate list within the same sheet once the choices are selected and a checkbox at the end of the row is marked, then clear the initial row of choices.
It's only 1 row (row 3) with 5 columns of data (Col A, B, C, and D/E merged and the checkbox in F). I would like the data to be moved to row 10 and continue down the list each time data is entered and the box is checked in row 3.
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "WarMistakes" && r.getColumn() == 6 && r.getValue() == true) {
var row = r.getRow();
var targetSheet = ss.getSheetByName("WarMistakes");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 9, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
Try this:
function onEdit(e) {
const row = e.range.getRow();
const col = e.range.getColumn();
const s = e.source.getSheetByName("SourceSheet");
const ts = e.source.getSheetByName("WarMistakes");
const as = e.source.getActiveSheet();
if(as.getName() == "SourceSheet" && col == 6 && row == 3 && as.getRange(row,col).getDisplayValue() === "TRUE") {
const data = s.getRange(row, 1, 1, 5).getValues();
if(ts.getLastRow()>=10){ s.getRange(s.getLastRow()+1,1,data.length,data[0].length).setValues(data);}
else{ts.getRange(10,1,data.length,data[0].length).setValues(data);}
s.getRange(3,6).setValue("FALSE");
s.getRange(row, 1, 1, 5).clearContent();
}
}
Explanation:
As soon as you click on cell F3, the data in SourceSheet from A-D/E are moved to WarMistakes row 10 and the script unticks the checkbox.
If there is a content in row 10, then it appends it right after and so on.
Please use the same sheet names that I used: SourceSheet, WarMistakes or adjust the code to your own needs.
Step 1 (you click the checkbox in cell F3 in SourceSheet):
Step 2 (data was moved to row 10 in WarMistakes):

How can I copy a range to a different sheet based on checkbox value, then delete on the new sheet, while keeping both check boxes up to date

I need some help on this code. I have my source sheet "Agency List". When the check box in column J is True the data in columns K-Last Column is copied to sheet "Itinerary" starting at the first empty row and column K. This formula is working, however I need to add some functionality and can't find an answer. I need to:
I want to be able to check the box, have the row copy to "Itinerary". Then on "Itinerary" have a checked box carry over with the data. When I uncheck that box on "Itinerary" I want to data to clear, and the corresponding check box on "Agency List" to be unchecked as well.
I also need to be able to toggle both check boxes with my mouse. And it seems that if the cell contains a formula you can not toggle. Is there a way around this?
Currently I'm copying the data to the last row in "Itinerary". But if there is a formula or column anywhere else on the sheet in that row it treats the row as not empty. In particular I can't have a checkbox in column 'Itinerary'!J:J down the sheet because then the row isn't blank and the data goes to the very bottom. I tried using a code that would look at the last row of Column O, but then they entire formula stopped working:
var target = targetSheet.getRange(targetSheet.getRange(O:O).getLastRow() + 1,11);
I thought about using VLOOKUP or Match or some other formula to scan a column in "itinerary" for a match and if there isn't a match then the checkbox goes to FALSE. But again, then I can't toggle the checkbox.
Is there a way to keep both check boxes up to date and copy/clear the data on "Itinerary" based on that checkbox?
Thanks!
Here is a link to a sample sheet:
Sample Sheet
function onEdit(event) {
// assumes source data in sheet named "Agency List"
// target sheet of move to named "Itinerary"
// getColumn with check-boxes is currently set to colu 3 or C
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if (s.getName() == "Agency List" && r.getColumn() == 3 && r.getValue() == true) {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Itinerary");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 11);
s.getRange(row, 4, 1, numColumns).copyTo(target, { contentsOnly: true });
} else if (s.getName() == "Itinerary" && r.getColumn() == 9 && r.getValue() == false) {
var row = r.getRow();
var numColumns = s.getLastColumn();
s.deleteRow(row);
}
}
Ok #Sio I think I made a fix that could work for you.
Basically the problem that you have is keeping track of what row in Itinerary corresponds to the row in Agency List.
To have that kind of persistence in Apps Script without actually storing in the sheet I found this handy Service called PropertiesService. In there I stored the rows in pairs to later retrieve.
Code
function onEdit(event) {
// assumes source data in sheet named "Agency List"
// target sheet of move to named "Itinerary"
// getColumn with check-boxes is currently set to colu 3 or C
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
var row = r.getRow();
var numColumns = s.getLastColumn();
Logger.log(r.getValue());
if (s.getName() == "Agency List" && r.getColumn() == 3 && r.getValue() == true) {
// Create the record in "Itnerary"
var prop = PropertiesService.getDocumentProperties();
var targetSheet = ss.getSheetByName("Itinerary");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 11);
s.getRange(row, 4, 1, numColumns).copyTo(target, { contentsOnly: true });
// Insert checkbox cell (already checked)
targetSheet.getRange(target.getRow(), 10).insertCheckboxes().check();
prop.setProperty(row, target.getRow());
} else if (s.getName() == "Agency List" && r.getColumn() == 3 && r.getValue() == "") {
// Remove the row from "Itinerary" when the checkbox in "Agency List" is unchecked
var prop = PropertiesService.getDocumentProperties();
var targetRow = prop.getProperty(row);
ss.getSheetByName("Itinerary").deleteRow(targetRow);
Logger.log("Deleted the " + targetRow + "row from " + s.getName());
prop.deleteProperty(row);
} else if (s.getName() == "Itinerary" && r.getColumn() == 10 && r.getValue() == false) {
// Remove the row from "Itinerary"when the checkbox is unchecked and unchecks in "Agency List"
var prop = PropertiesService.getDocumentProperties();
s.deleteRow(row);
// Look the corresponding row in "Agency List"
var keys = prop.getKeys();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (prop.getProperty(key) == row) {
ss.getSheetByName("Agency List").getRange(key, 3).setValue(false);
prop.deleteProperty(key)
break;
}
}
}
}
Explanation
So you actually have three cases you need to process here.
You check the row in Agency List to create a new record in Itinerary.
if (s.getName() == "Agency List" && r.getColumn() == 3 && r.getValue() == true) {
// Create the record in "Itnerary"
var prop = PropertiesService.getDocumentProperties();
var targetSheet = ss.getSheetByName("Itinerary");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 11);
s.getRange(row, 4, 1, numColumns).copyTo(target, { contentsOnly: true });
// Insert checkbox cell (already checked)
targetSheet.getRange(target.getRow(), 10).insertCheckboxes().check();
prop.setProperty(row, target.getRow());
}
This is the one you already had, just adding the insertCheckboxes at 10th column (J). Also storing the pair of rows to later retrieve it. Look that I have store it in a document level.
You uncheck the row at Agency List to delete the row at Itinerary
} else if (s.getName() == "Agency List" && r.getColumn() == 3 && r.getValue() == "") {
// Remove the row from "Itinerary" when the checkbox in "Agency List" is unchecked
var prop = PropertiesService.getDocumentProperties();
var targetRow = prop.getProperty(row);
ss.getSheetByName("Itinerary").deleteRow(targetRow);
Logger.log("Deleted the " + targetRow + "row from " + s.getName());
prop.deleteProperty(row);
}
Getting the row we need to delete through the document properties. Then use the deleteRow in Itinerary, and get rid of the record in our properties with deleteProperty.
You uncheck the row in Itinerary to delete that row and to uncheck the row in Agency List .
} else if (s.getName() == "Itinerary" && r.getColumn() == 10 && r.getValue() == false) {
// Remove the row from "Itinerary"when the checkbox is unchecked and unchecks in "Agency List"
var prop = PropertiesService.getDocumentProperties();
s.deleteRow(row);
// Look the corresponding row in "Agency List"
var keys = prop.getKeys();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (prop.getProperty(key) == row) {
ss.getSheetByName("Agency List").getRange(key, 3).setValue(false);
prop.deleteProperty(key)
break;
}
}
}
In here deleting the row is pretty straightforward because you retrieve that from the event object. Although in this case we will need to iterate to every pair of values from the properties because we stored the key as the row of Agency List
Reference
Storing and retrieving data in Apps Script
getDocumentProperties()
deleteProperty
insetCheckboxes()

GoogleScript: How to CopyRow before Delete the same

Situation:
I find this script, that delete the rows when the text in the column B change to "Closed". I want to copy the row before to delete into other sheet called historic.
Action:
The script should be copy the row when the the status is "Closed" in other sheet called "Historic" in the same spreadsheeet.
Then when the first point is completed should be delete the rows in the activesheet.
SCRIPT DELETE ROWS:
function DeleteRow(e) {
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() == 'ISP1' && // change to your own
e.range.columnStart == 2 && e.range.columnEnd == 2 && // only look at edits happening in col 2 which is 2
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 == 'Closed') { // Delete if value is zero or empty
e.source.getActiveSheet().deleteRow(e.range.rowStart);
}
}
SCRIPT HISTORIC:
This script when somone update the column 5 will copy the row to other sheet called "historic".
When somone change the column 9 and select the status "Closed" the script copy the row to the historic and insert the closed date.
Now, does exist any why to do the same and improve the perfomance? I think this script is very complex and use many line.. I don't know if possible do the same better.
function Historic(e) { //CopyRows
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = SpreadsheetApp.getActiveRange();
var date = Utilities.formatDate(new Date(), "GMT-3", "dd/MM/yyyy HH:mm"); // Function Date + Format
// Get the row and column of the active cell.
var rowIndex = r.getRowIndex();
var colIndex = r.getColumnIndex();
// Get the number of columns in the active sheet.
var colNumber = s.getLastColumn();
// Move row based on criteria in column 5, and if row is not the header.
if (colIndex == 5 && rowIndex != 1) {
// Get value from column 5, in the active row.
var status = s.getRange(rowIndex, colIndex).getValue();
// Do nothing if criteria value is not actually changed to something else.
if (s.getName() != 'Historic') {
// The target sheet is the one with the same name as the criteria value.
var targetSheet = ss.getSheetByName('Historic');
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(rowIndex, 1, 1, colNumber).copyTo(target);
//s.deleteRow(rowIndex); // This line delete the source row
targetSheet.getRange(targetSheet.getLastRow(), 11).setValue(s.getSheetName()); //Insert in the Column 11 the SheetName where was modify that line
}
}
else if (colIndex == 9 && rowIndex != 1 && e.value == 'Closed' ) {
// Get value from column 5, in the active row.
var status = s.getRange(rowIndex, colIndex).getValue();
// Do nothing if criteria value is not actually changed to something else.
if (s.getName() != 'Historic') {
// The target sheet is the one with the same name as the criteria value.
var targetSheet = ss.getSheetByName('Historic');
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(rowIndex, 1, 1, colNumber).copyTo(target);
targetSheet.getRange(targetSheet.getLastRow(),10).setValue(date); //This Line Insert Close Date when the user select option Close
targetSheet.getRange(targetSheet.getLastRow(),11).setValue(s.getSheetName()); //Insert in the Column 11 the SheetName where was modify that line
e.source.getActiveSheet().deleteRow(e.range.rowStart); // This line delete the source row
}
}
}
I see somone vote this question -1. I'm using the Historic the second part, this part copy and then delete the row.
If anyone have a better idea, I will appreciate your help.
Thanks So much