Time trigger to cut & paste row when cell equals N/A - google-apps-script

I have a spreadsheet with the following 2 sheets:
Sheet1 "Induction Checklist" is a list of current staff (around 400) from A3:M - Row 2 is the header row.
Sheet2 "Induction Checklist - Staff Left" is a duplicate of Sheet1 but a list of previous staff from A3:M - Row 2 has the same headers as Sheet1
Column C is the Staff ID (number) in both sheets.
Column C of Sheet1 has an ImportRange/Vlookup formula to an entirely different spreadsheet (Source Sheet) to find the Staff ID. When the employee is removed from the Source Sheet, the formula can't find the employee so the value in column C of Sheet1 changes to "N/A".
I want to create a time-based trigger to find the "N/A" values in column C of Sheet1, then cut and paste only those rows to Sheet2 and delete the same rows from Sheet1. If "N/A" is not found, do nothing.
This is a sample of my spreadsheet:
Sample Spreadsheet
I have the following script but can't get it to work. I would really appreciate some help.
function removeOldStaff() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Induction Checklist");
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var column = 3
var errorColumn = range.getColumn();
var errorRow = range.getRow();
var error = range.getValue();
var value = "N/A"
if(error == value && errorColumn == column && errorRow > 3 && sheet.getName() == sheetName) {
var numCols = sheet.getLastColumn();
var row = sheet.getRange(errorRow, 1, 1, numCols).getValues();
var destinationSheet = ss.getSheetByName("Induction Checklist - Staff Left");
// Get first empty row:
var emptyRow = destinationSheet.getLastRow() + 1;
// Copy values from 'Induction Checklist'
destinationSheet.getRange(emptyRow, 1, 1, numCols).setValues(row);
sheet.deleteRow(errorRow);
}
}

Issue:
You are not iterating through the different rows in your range. For example, range.getValue() will only return the value of the top-left cell in the range, in your case A1. That's hardly what you want to look for. Similarly, functions like getColumn() or getRow() don't make sense in this context.
Solution:
Use getValues() to get all the values in your range in a 2D array, and iterate through that (for example, with for).
For each row, check the value in column C.
If the value is #N/A, push the full row of data to and array with all the rows to copy (dataToCopy in the sample below), and delete the row from the source sheet (you should loop in reverse order because otherwise deleting the rows would mess with the row indexes).
Copy dataToCopy to the target sheet via setValues
Code sample:
function removeOldStaff() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Induction Checklist");
var destinationSheet = ss.getSheetByName("Induction Checklist - Staff Left");
var firstRow = 3;
var column = 3;
var value = "#N/A";
var values = sheet.getRange(firstRow, 1, sheet.getLastRow() - firstRow + 1, sheet.getLastColumn()).getValues();
var dataToCopy = [];
for (var i = values.length - 1; i >= 0; i--) {
var row = values[i];
var errorValue = row[column - 1];
if (errorValue == value) {
dataToCopy.push(row);
sheet.deleteRow(firstRow + i);
}
}
// Get first empty row:
var emptyRow = destinationSheet.getLastRow() + 1;
// Copy values from 'Induction Checklist'
destinationSheet.getRange(emptyRow, 1, dataToCopy.length, dataToCopy[0].length).setValues(dataToCopy);
}

Related

Copy cells to second sheet based on cell value using a script

We use google sheets for our invoice system. Once we pull the order, we fill in the invoice and anything that we do not have we backorder. We type BO in column I. In excel, we used a button that we could click to copy those cells to our 2nd sheet. We need cells A & B to copy to sheet 2 (which is an exact copy of sheet 1) if column I says BO. Here's what I have so far. This almost works... It deletes everything above the rows with data though and copies the data even if column I doesn't have BO.
I need it to just copy Column A & B if column I says BO to sheet 2 (which is a duplicated of sheet 1) I'm sure there's a simple way, but I can't seem to figure it out.
function Backorder() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Carolina Fireworks Order Form");
var columnToSearch = 9; // I column
Logger.log(sheet.getLastRow());
var range =sheet.getRange(1,columnToSearch,sheet.getLastRow(),columnToSearch);
var destination = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Back Order");
var values = range.getValues();
//Destination
var numberOfColumns = 12;
var numberOfRows = 223 ;
var startColumn = 1;
var startRow = 12;
// equivalent to destination.getRange('A1:L223');
var destinationRange= destination.getRange(startRow, startColumn,numberOfRows,numberOfColumns);
var count = '0';
for(var i=0;i<values.length;i++){
Logger.log("i is now: " +i);
Logger.log("Current row value: " +values[i][0])
if(values[i][0] == 'BO'){
Logger.log("*** Value found in column: " +values[i][0] +"**** \n Count: " +i);
count++;
var rangeToCopy = sheet.getRange(i, 1,numberOfRows,numberOfColumns);
rangeToCopy.copyTo(destinationRange);
}
}
}
SOLUTION
**Updated **
The updated script below will loop through the sourceSheet on every row and it specifically checks if every row on column I says "BO".
If there's a match on that specific row of column I, the row # from the loop will be added to rangeToCopy variable (e.g. range "A(row #):B(row #)" in A1 notation). Then, the rangeToCopy will be copied to the destination in the same exact range of rangeToCopy, given that the destination is the same exact copy of sourceSheet.
function backOrder(){
var sheet = SpreadsheetApp.getActive();
var sourceSheet = sheet.getSheetByName("Carolina Fireworks Order Form");
var destination = sheet.getSheetByName("Back Order");
var lastRow = sourceSheet.getDataRange().getLastRow();
//LOOP THROUGH SOURCE SHEET
for(var row=1; row<=lastRow; row++){
//CHECK FOR "BO" ON EVERY ROWS OF COL I ON SOURCESHEET
if(sourceSheet.getRange(row,9).getValue() == "BO"){
Logger.log("CELL I"+row+" has \"BO\""+"\n=================\n"+"RANGE TO COPY:\n"+ "\"A"+row+":B"+row+"\"");
var rangeToCopy = "A"+row+":B"+row;
sourceSheet.getRange(rangeToCopy).copyTo(destination.getRange(rangeToCopy));
sourceSheet.getRange(row,9).setValue("- BO").setHorizontalAlignment("right");
}
}
}
RESULT
Sample filled out Carolina Fireworks Order Form sheet:
The Back Order sheet after running the script:
Execution logs result for review:

Google Sheets Scripts - For each cell in range, get row number where cell has specific value, copy row to first blank row in range

I come from a VB background and am having some trouble implementing a scripts solution for Sheets, I imagine the function is fairly basic but am not yet sure how to express it in JS. The goal is to loop through each cell in a range (Sheet 1, Q3:Q1000), get the row number where the cell in that range has the value "TRUE", copy/cut the entirety of each row on Sheet 1 that meets that qualification (or simply store the values and skip the copy/paste step), paste the values to the first unused row on a separate sheet (Sheet2), then delete the original row on Sheet1.
So far, I have managed to put together this crude function which successfully finds the row number of the first cell in the given range containing the specified value, but it returns NaN if there is more than one occurrence of the value in the range. Any indication as to what I am doing wrong will be helpful, thank you.
function onEditTesting(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Sheet1");
const range = "Q3:1000";
var whereTrue = "TRUE";
var range = sheet.getRange("Q3:Q1000");
var rangeValues = sheet.getRange(range).getValues();
var i = [];
for (var y = 0; y < rangeValues.length; y++) {
if(rangeValues[y] == whereTrue){
i.push(y);
}
}
var Row = Number(i)+Number(range.getRow());
}
function moveData() {
const ss=SpreadsheetApp.getActive();
const ssh=ss.getSheetByName('Sheet1');
const dsh=ss.getSheetByName('Sheet2');
const vs=ssh.getRange(3,1,ssh.getLastRow()-2,ssh.getLastColumn()).getValues();
let oA=[];
let d=0;
vs.forEach((r,i)=>{
if(r[16]=='TRUE') {
oA.push(r);
ssh.deleteRow(i+3-d++);
}
});
dsh.getRange(dsh.getLastRow()+1,1,oA.length,oA[0].length).setValues(oa);
}
Here is another approach using onEdit() Simple Trigger.
Sample Code:
function onEdit(e) {
var ss = e.source;
var cell = e.range;
var sourceSheet = ss.getActiveSheet();
var destinationSheet = ss.getSheetByName("Sheet2");
var col = cell.getColumn();
var row = cell.getRow();
var lastCol = sourceSheet.getLastColumn();
Logger.log("row: "+row+" maxCol: "+lastCol+" col: "+col+" val: "+cell.getDisplayValue()+" sheet: "+sourceSheet.getName());
//Check if modified cell is in column Q (index 17) in Sheet1 has a value of TRUE
if(sourceSheet.getName() == "Sheet1" && col == 17 && cell.getDisplayValue() == "TRUE"){
//Append current row to the destination sheet
var values = sourceSheet.getRange(row,1,1,lastCol).getValues();
Logger.log(values);
destinationSheet.appendRow(values.flat());
//Delete current row
sourceSheet.deleteRow(row);
}
}
What it does?
Get the sourceSheet and cell modified using e.range event object
Check the cell modified if it is in column Q (index 17) of Sheet1 having a value of "TRUE". I used Range.getDisplayValue() to get the actual string value of the cell because when I test it using Range.getValue() it gave me a boolean object
Get the row values from the first column up to the last column of the current row using Sheet.getLastColumn() and Sheet.getRange(row, column, numRows, numColumns)
Append the current row values of the source sheet to your destination sheet using Sheet.appendRow(rowContents) where rowContents is a 1-d array and since the Range.getValues() returns a 2-d array, I used array.flat() to convert 2-d array to 1-d array
Delete the current row using Sheet.deleteRow(rowPosition)
Output:
Before (Sheet1):
After (Sheet1):
After (Sheet2):

Google Script: copy values from one sheet to another determining the range

I have two sheets with unique IDs in one column. I need to make sure whenever a new ID is added in one sheet (Sheet1), it is copied to the last empty row in the other sheet (Sheet2). The IMPORTRANGE won't work as being dynamic any static information added in the other sheet would be irrelevant to the respective ID.
In another thread [1], I got help developing this script that will do exactly that. However, this script will insert the data in the other sheet in Column A. How can I modify the script so I can decide the specific range where I want the script to insert the data (in Sheet 2).
Update: I created this spreadsheet as an example. I'm trying to ensure that the script does the calculation (ie. adding vlaues that are not duplicated in the first available empty row), starting in cel C10 in "Sheet2". It would be also great if I can somehow change that range if I need to: https://docs.google.com/spreadsheets/d/1abESAXFrOHoqRQRNqQGfmAFxlbD10wlprAf1tca4y7o/edit#gid=132361488
Thanks!
function updateSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = "Sheet1";
var destinationSheet = "Sheet2";
var source_sheet = ss.getSheetByName(sourceSheet);
var target_sheet = ss.getSheetByName(destinationSheet);
var lastCol = target_sheet.getLastColumn();
var lastRow = target_sheet.getLastRow();
if (lastRow > 1) { // <--- Added
var r = target_sheet.getRange(2,1, lastRow - 1, lastCol);
r.sort([{column: 1, ascending: true}]);
}
_updateSpreadsheet(source_sheet, target_sheet);
}
function _updateSpreadsheet(source_sheet, target_sheet) {
var last_row = target_sheet.getLastRow();
var source_data = source_sheet.getRange("A4:A" + source_sheet.getLastRow()).getValues(); // <--- Modified
var target_data = target_sheet.getDataRange().getValues();
var resultArray = [];
for (var n = 0 ; n < source_data.length ; n++) { // <--- Modified
var keep = true;
for(var p in target_data) {
if (source_data[n][0] == target_data[p][0]) {
keep = false; break;
}
}
var columnsToKeep = [0];
var tempData = [];
if(keep){
for(var c in columnsToKeep){ tempData.push(source_data[n][columnsToKeep[c]])}
resultArray.push(tempData);
}
}
last_row++;
resultArray = resultArray.filter(String); // <--- Added
if(resultArray.length>0){
target_sheet.getRange(last_row,1,resultArray.length,resultArray[0].length).setValues(resultArray);
}
}
[1] Google Script: Append new values in column to another sheet
target_sheet.getRange(last_row,1,resultArray.length,resultArray[0].length)
.setValues(resultArray);
This line
Takes the sheet stored in the variable target_sheet
Accesses the defined range
Sets values
The syntax to access a range used in your code is getRange(row, column, numRows, numColumns)
whereby the start column is numerical - in your case it's 1 which corresponds to column A.
If you want to modify the start column from A to B:
Just change your range definition from
getRange(last_row,1,resultArray.length,resultArray[0].length)
to
getRange(last_row,2,resultArray.length,resultArray[0].length)

Need to return the displayed value not formula when copying a row between sheets

Im currently using google sheets and a script to move a portion of row when i select "ok" in a data validation column , the problem is that it copies the formulas of each cell and not the displayed value, and ideas, Im not the best at this so any help is HUGE.
/**
* Moves row of data to another spreadsheet based on criteria in column 6 to sheet with same name as the value in column 4.
*/
function onEdit(e) {
// see Sheet event objects docs
// https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
var ss = e.source;
var s = ss.getActiveSheet();
var r = e.range;
// to let you modify where the action and move columns are in the form responses sheet
var actionCol = 6;
var nameCol = 4;
// 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.
// -1 to drop our action/status column
var colNumber = s.getLastColumn()-1;
// if our action/status col is changed to ok do stuff
if (e.value == "ok" && colIndex == actionCol) {
// get our target sheet name - in this example we are using the priority column
var targetSheet = s.getRange(rowIndex, nameCol).getValue();
// if the sheet exists do more stuff
if (ss.getSheetByName(targetSheet)) {
// set our target sheet and target range
var targetSheet = ss.getSheetByName(targetSheet);
var targetRange = targetSheet.getRange(targetSheet.getLastRow()+1, 1, 1, 6); //6 represents Numer of Columns to Copy
// get our source range/row
var sourceRange = s.getRange(rowIndex, 1, 1, 6); //6 represents Numer of Columns to Copy
// new sheets says: 'Cannot cut from form data. Use copy instead.'
sourceRange.copyTo(targetRange);
// ..but we can still delete the row after
// or you might want to keep but note move e.g. r.setValue("moved");
}
}
}
Add the option to sourceRange.copyTo(targetRange); as described here:
https://developers.google.com/apps-script/reference/spreadsheet/range#copyTo(Range,Object)
Please look at the docs before posting, thanks. Good luck.

Google Docs SpreadSheet Script - How do I reference a specific Cell in a row as a condition

I have the following script (some of which I found and modified to my needs:
function onedit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var targetSheetName = "Completed";
var sourceSheetName = "Eligibility & Pre-Auths"
var targetSheet = ss.getSheetByName(targetSheetName);
var sourceSheet = ss.getActiveSheet();
var sourceRow = sourceSheet.getActiveRange().getRow();
var targetRow = targetSheet.getLastRow() + 1;
if (sourceSheet.getName() == SourceSheetName) {;
if (sourceRow[22]=="Yes") {;
if (targetRow > targetSheet.getMaxRows()) targetSheet.insertRowAfter(targetRow - 1);
sourceSheet.getRange(sourceRow, 1, 1, sourceSheet.getLastColumn()).copyTo (targetSheet.getRange(targetRow, 1));
sourceSheet.deleteRow(sourceRow);
};
};
}
Everything seems to working except my condition - if (sourceRow[22]=="Yes")
I am new to this, so I know I am just not referencing it correctly.
I am just trying to check if the text "Yes" is in column 22 of the current row. I am executing this script on edit. So basically, when a "Yes" is entered in this fieild, I am moving this record to the sheet called "Completed"
the statement getRow() returns an integer, the number of the row of that range, what you want is the value of the 22cd cell in that row , so try
var sourceRow = sourceSheet.getActiveRange().getRow();
var sourceRowValue = sourceSheet.getRange(sourceRow,1,1,sourceSheet.getMaxColumn()).getValues();
and then to get the cell value condition :
if (sourceRowValue[0][21]=="Yes") {;
...
The result of getValues() is always a 2 dimension array, that's the reason for the [0] and is 0 indexed >> changed to 21
Note that I guess your code could be simpler if you use sourceSheet.getActiveCell().getValue() which returns directly the value of the cell you are writing to...
try this small example where I get the Value and the column index:
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var cellValue = ss.getActiveCell().getValue()
ss.getRange('A1').setValue(cellValue+' in col '+ss.getActiveCell().getColumn())
}