Automatically move data from one sheet to another in google docs - google-apps-script

i have a spreadsheet that i keep track of tasks i need to do, once complete i enter a date in the last column. What i want is for that completed task to be moved to sheet 2.
At present i have sheet 1 named SUD_schedule and i want the completed row of data to be moved to sheet 2 named SUD_archive. I've looked through the forum posts already and i've tried a variation of scripts but so far no luck. The closest i have come is this script:
function onEdit() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();//Original sheet
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];//target sheet
// to act on only one sheet, check the sheet name here:
//If it si not first sheet, it will do nothing
if (sheet1.getSheetName() != "SUD_schedule") {
return;
}
//Get Row and column index of active cell.
var rowIndex = sheet1.getActiveRange().getRowIndex();
var colIndex = sheet1.getActiveRange().getColumnIndex();
//If the selected column is 10th and it is not a header row
if (colIndex == 16 && rowIndex > 1) {
//Get the data from the current row
var data = sheet1.getRange(rowIndex,1,1,9).getValues();
var lastRow2;
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
//Copy the data to the lastRow+1th row in target sheet
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
}
}
Column P (16) is the task complete date, row 1 is frozen and contains column headers.
Can anybody help show where i'm going wrong.
Kind regards
Den

Your code is not generic and you are more complicating your objective. Below will work out your need.
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('SUD_schedule');
var sheet2 = ss.getSheetByName('SUD_archive');
var dateColumn = "16";
var array = []
var range = sheet1.getRange(1, 1, sheet1.getLastRow(), dateColumn);
for (var i = 2; i <= sheet1.getLastRow(); i++) //i iterates from 2 as you say R1 is header
{
if(isValidDate(range.getCell(i, dateColumn).getValue()) == true) //checking if any values on column16 is valid date
{
data = sheet1.getRange(i, 1, 1, dateColumn).getValues(); //Getting the range values of particular row where C16 is date
for (var j = 0; j < dateColumn; j++) //Adding the row in array
{
array.push(data[0][j]);
}
}
if(array.length > 0)
{
sheet2.appendRow(array); //Appending the row in sheet2
array = [];
sheet1.deleteRow(i); //deleting the row in sheet as you said you want to move, if you copy remove this and next line
i=i-1; //managing i value after deleting a row.
}
}
}
//Below function return true if the given String is date, else false
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}

I am not sure that the syntax you have as used below is entirely correct.
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
What I know will work for certain is if you omit the variable lastRow2 all together and use this instead.
sheet2.getRange(getLastRow+1,1,1,data[0].length).setValues(data);

To complement Joachin's answer, here is how you can adapt that code if you don't have the date in the last row. In the below shown part of the code replace Lastcolumnumber with your last column.
//Getting the range values of particular row where C16 is date
data = sheet1.getRange(i, 1, 1, LASTCOLUMNNUMBER).getValues();
//Adding the row in array
for (var j = 0; j < LASTCOLUMNNUMBER; j++)

Related

How to delete cells if no match was found?

I'm having difficulties writing the IF statement to satisfy my condition. I have 2 sheets: Main and Logistics. The first one has specific information about cargo and it's transportation, such as trailer, position and arrival date. The second sheet contains all of the transportation information, such names of the trailer, drivers, arrivals, departures etc. Based on the scheduled trailers on sheet "Logistics", the user can specify which of the available trailers he wants to use for the cargo in question.
However, in a situation when the trailer gets deleted from "Logistics" due to a cancellation, I am unable to revoke previously made selection on "Main". My idea is to make this script look for matching combinations of Destination and Trailer on both sheets (columns 8 and 13 on Main, columns 1 and 2 on Logistics). If there is a row on "Main" for which no matching trailer with the same destination was found on "Logistics", the script should set columns 13, 14 and 16 on "Main" to empty.
Could someone help me write an IF statement to satisfy this condition?
I have attached the bit of code I have so far. I think only IF statement needs modifying. Thanks for the help!
function deleteSelection() {
// Main variables:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetMain = ss.getSheetByName("Main");
var tabMain = sheetMain.getRange(2, 1, sheetMain.getLastRow(), 18);
var dataMain = tabMain.getValues();
// Logistics variables:
var sheet = ss.getSheetByName("Logistics");
var dataRange = sheet.getRange(2, 1, sheet.getLastRow(), 9);
var data = dataRange.getValues();
for(var i = 0; i < dataMain.length; i++){
for(var j = 0; j < data.length; j++){
// Compare data: if there is no match between 2 sheets, set "Trailer", "Position" and "Arrival date" to empty:
if(dataMain[i][7].toLowerCase() == data[j][0].toLowerCase() && dataMain[i][12] == data[j][1]){
} else{
dataMain[i][12] = "";
dataMain[i][13] = "";
dataMain[i][15] = "";
}
}
}
// Take the modified tab and put it on the spreadsheet
tabMain.setValues(dataMain);
}
UPD: Added a sample link. On "Logistics" you can see crossed out row, upon delete of which, a script should delete crossed out rows on "Main".
You are trying to establish whether the Event and Trailer values on Main match a value on Logistics.
I have taken a slightly different approach to you. I concatenated the value of the Main "EVent" and "Trailer" and used that value to find the match on Logistics.
If a Match is found, then the script can break out of that loop and proceed to the next loop.
If no Match is found, then a variable is incremented (since the match may be found in a subsequent comparison). However, once all the values on Logistics have been evaluated, if the number of mismatches is equal to the number of records on Logistics, then the values on Main must be updated.
function so5992862301() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetMain = ss.getSheetByName("Main");
var tabMain = sheetMain.getRange(2, 1, sheetMain.getLastRow()-1, 18);
var dataMain = tabMain.getValues();
// Logger.log("DEBUG: Last row = "+sheetMain.getLastRow()+", length of dataMain = "+dataMain.length+" tab main = "+tabMain.getA1Notation());
// Logistics variables:
var sheet = ss.getSheetByName("Logistics");
var dataRange = sheet.getRange(2, 1, sheet.getLastRow()-1, 9);
var data = dataRange.getValues();
// Logger.log("DEBUG: Logistics Last row = "+sheet.getLastRow()+", length of data = "+data.length+" dataRange = "+dataRange.getA1Notation());
// start loop through Main
for(var i = 0; i < dataMain.length; i++){
// count the matches
var mismatch=0
// start loop through Logistics
for(var j = 0; j < data.length; j++){
// match Logistics: Event (Column A) and Trailer (Column B)
// match Main: Event (Column A) and Trailer (Column C)
// Compare data: if there is no match between 2 sheets, set "Trailer", "Position" and "Arrival date" to empty:
var logEventTrailer = data[j][0]+data[j][1];
var mainEventTrailer = dataMain[i][0]+dataMain[i][2];
//Logger.log("DEBUG: i:"+i+", Main:"+mainEventTrailer+", j:"+j+" Log:"+logEventTrailer);
if (mainEventTrailer === logEventTrailer){
// match
// Logger.log("DEBUG: Match-"+"i:"+i+", Main:"+mainEventTrailer+", j:"+j+" Log:"+logEventTrailer);
// if this is a match, then break loop and goto to next i
break;
}
else{
// no match
mismatch = mismatch+1
//Logger.log("DEBUG: No match:"+match+"-i:"+i+", Main:"+mainEventTrailer+", j:"+j+" Log:"+logEventTrailer)
}
// how many mismatches
if (mismatch ==data.length){
// no match found
//Logger.log("DEBUG: no match found");
// update array values for this row
dataMain[i][2] = "";
dataMain[i][3] = "";
dataMain[i][4] = "";
}
}
}
// update the array values for Main
tabMain.setValues(dataMain);
}

Auto insert row above, based on single cell value

I am trying to auto-insert a row above the current top row (row 2) based on a cell's value.
I cannot figure out the variables I need to put into the script editor.
I tried googling for help and modified other peoples script to see if I could do it, and the answer is no, no I do not possess the knowledge/skill.
function conditionalNewRow() {
var ss = SpreadsheetApp.getActive().getSheetByName('NEW INV');
var sh = ss.getActiveSheet();
var headerRow = 1;
var firstRow = headerRow + 1;
var range = sh.getRange("A2"); // Get range of row 2.
var futureRange = sh.getRange("A3"); // Get range of row 3.
var numCols = range.getNumColumns(); // Get the number of columns for row 2.
var contentVal = false;
for (i = 1; i <= numCols; i++) {
var currentValue = range.getCell(1,i).getValue();
if (currentValue == "NO"){
contentVal = true;
break;
}
}
if (contentVal == true) {
sh.insertRowBefore(firstRow); // Insert an empty row into row 2.
futureRange.copyTo(sh.getRange(firstRow, 1, firstRow, numCols), {contentsOnly:false}); // Copy row 3 to row 2.
}
}
All I want is a blank row above the previous row when a cell meets certain criteria;
e.g. Cell A2 contains any of the following; YES, NO, LOADED, UNLOADED
If it contains any of those values, it will auto-insert a row above.
I am a little unclear on your intent and what issues you are having.
A few things I see:
1) Since your range is a single cell, var numCols = range.getNumColumns(); should always return 1, so your loop, for (i = 1; i <= numCols; i++), should run exactly once. It feels like there is a lot of redundant code here.
To get the value of cell A2, you could just write:
var range = sh.getRange("A2");
var currentValue = range.getValue();
2) From the if statements, it looks like you only want to add a new row if the value of A2 is something other than "NO". Is that correct? (This is not what your question states). If so, I think you're pretty close with the sh.insertRowBefore(firstRow); statement, it just might be getting lost in some convoluted logic.
3) Do you really want to copy everything from row 3 into row 2? In the question you state "All I want is a blank row above the previous row when a cell meets certain criteria".
Perhaps something like this is closer to what you want?
function conditionalNewRow() {
var ss = SpreadsheetApp.getActive().getSheetByName("NEW INV");
var sh = ss.getActiveSheet();
var range = sh.getRange("A2"); // Get range of row 2.
var currentValue = range.getValue();
if (currentValue !== "NO") {
sh.insertRowBefore(firstRow); // Insert an empty row into row 2.
}
}
Edit
From your comment, sounds like you want to insert a new row whenever A2 is edited to one of the selectable values. You may want to check out simple triggers, such as onEdit, which runs whenever a cell is edited. For instance, something like this:
/**
* The event handler triggered when editing the spreadsheet.
* #param {Event} e The onEdit event.
*/
function onEdit(e) {
// get sheet
var sheet = SpreadsheetApp.getActive().getSheetByName("NEW INV");
// Get the edited range
var editedRange = e.range;
// check if cell A2 was edited
if (editedRange.getA1Notation() === "A2") {
// check if value is a desired value
if (editedRange.getValue() === "YES" || "NO" || "LOADED" || "UNLOADED") {
// if yes to both, insert new row
sheet.insertRowBefore(2);
}
}
}

Script - Add a row beneath, based on conditional criteria

Dear Oracles of the script,
I have been trying to get some script to automatically add a row beneath the one I have inputted on, but only if the Balance is anything BUT zero. If it's zero, I don't want another row adding.
I've tried a few scripts and looked around the site, and tried them with triggers with on edit, but they just seem to add a row, despite me trying to state a condition for them to trigger.
function onEdit(event) {
var eventRange = event.range;
if (eventRange.getColumn() == 12) { // 12 = column of input that triggers it
var columnXRange =
SpreadsheetApp.getActiveSheet().getRange(eventRange.getRow(), 13,
eventRange.getNumRows(), 12); /// column number it affects
var values = columnXRange.getValues();
for (var i = 0; i < values.length; i++) {
if (!values[i][0]) { // If cell isn't empty
values[i][0] = '0';
}
}
columnXRange.setValues(values);
}
}
function Clear(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('AGL');
sheet.getRange('N4:N').clearContent();
}
function AddRow() {
var sheet = SpreadsheetApp.getActiveSheet();
if (sheet.getName() == "AGL") {
var activeCell = sheet.getActiveCell();
if (activeCell.getColumn() == 13) {
var Balance = sheet.getRange('N:N');
if (Balance != '0'); {
sheet.insertRowAfter(activeCell.getRow());
}
}
}
}
In column N, I have an array formula working out the balance of stock in, against stock out.
In column M, I have a script running that will add a 0 when something is put into column L. I also have a script that erases the output of the array formula, so they don't get tangled up with each other.
I would like, when editing a row, when I place a figure in column L or M, and if the balance in Column N is greater than 0, I would like a new row adding underneath. (If you can get it to add the values from A & B, that'd be a bonus, but not fussy.) If the balance is 0, I don't want a row adding.
Currently, I have mixed results with it just adding a row every time I edit column N.
Requirement:
Add a row when columns L or M are edited and the value in column N is greater than 0 (& if possible, add values from columns A & B to the new row).
Solution:
This is pretty much all you need for your script. I've added comments to explain what each part is doing so it should be pretty easy to follow.
function onEdit(e) {
//define edited sheet, row number and column number
var sh = e.source.getActiveSheet();
var row = e.range.getRow();
var col = e.range.getColumn();
//get value of column N for edited row
var val = sh.getRange(row, 14).getValue();
//if columns L or M are edited and column N is greater than 0
if ((col === 12 || col === 13) === true && val > 0) {
//insert row below edited row
sh.insertRowAfter(row);
//get values of columns A and B of edited row
var vals = sh.getRange(row, 1, 1, 2).getValues();
//set values in columns A and B
sh.getRange(row+1, 1, 1, 2).setValues(vals);
}
}
Notes:
I haven't included any of your other functions, only adding a row, you can incorporate these in this function if you desire.
You won't be able to run this script manually at all (it'll fail on the first line), it'll just run automatically when the sheet is edited.
References:
Event Objects
Class Sheet

Output of GAS function not the individual cell value, but the full column of values

I'm new to Google Apps script. I created a Google Apps Script to act as a alternative for vlookup (I want static data for reasons).
I've created a function that is able to communicate between Sheet 1 "Main Sheet" (user input), and Sheet 2 "Query Sheet" (contains data results).
Intention is to compare mainSheet ColW with querySheet ColA
If mainSheet ColW matches, replace the value in mainSheet ColX with the value of querySheet ColB in the corresponding row.
Example if mainSheet Col W2 is '123' and querySheet Col A5 is '123', paste value from querySheet Col B5 into mainSheet Col X2
This is for comparing input provided by a user with data that is updated in the backend after X period of time. To reduce the need to constantly review this data, the query runs in intervals. The user sheet is populated with results.
function match(){
//Sheet with the query outputs
var querySheet = SpreadsheetApp.openById("SHEETID123456789"); // query results are dumped in this doc
var querySheetTab = querySheet.getRange("query!A1:F"); //tab with the results
//Main sheet to have query data pasted
var mainSheet = SpreadsheetApp.getActiveSpreadsheet(); //current worksheet
var mainSheetTab = mainSheet.getRange("USERSHEET!W2:X"); //Tab and range of the data. W contains a number. X is where the results of B from the querySheet go
//values to compare
var mainSheetVal = mainSheet.getSheetByName('USERSHEET').getRange('W2:W').getValues(); //values to match against query results
var mainSheetComp = mainSheet.getSheetByName('USERSHEET').getRange('W2:W'); //numbers to match against query results
var mainSheetSet = mainSheet.getSheetByName('USERSHEET').getRange('X2:X'); //Where values get pasted.
var getValQS = querySheet.getSheetByName('query').getRange('A1:B').getValues(); //query results includes both objects values
var valQS = querySheet.getSheetByName('query').getRange('A1:A'); // query results
var valSet = querySheet.getSheetByName('query').getRange('B2:B'); //desired values to paste.
var output = [];
for (var i = 0; i < mainSheetVal.length; i++) {
mainSheetSet.setValue(getValQS[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(10000);
if (mainSheetComp.getValue() == '') {
output.push([getValQS[i][0]]);
}
}
mainSheet.getRange(2, 12, output.length, 2).setValues(output);
}
This sheet ends up replacing X2:X with the value of A1, then A2, and continues to cycle through even with empty cells. I haven't been able to set-up a proper compare.
You want to compare the value of column "W" of USERSHEET sheet with the value of column "A" of query sheet.
When above both values are the same, you want to put the value of column "B" of query sheet to the cell of column "X" of USERSHEET sheet.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modification points:
Values of USERSHEET were retrieved from "W:X" by one call.
Values of USERSHEET were modified when the value of column "A" of query is the same with that of column "W" of USERSHEET.
After the values of USERSHEET were modified, the modified values were put to USERSHEET.
Modified script:
function match(){
var querySheet = SpreadsheetApp.openById("SHEETID123456789").getSheetByName("query");
var queryValues = querySheet.getRange(1, 1, querySheet.getLastRow(), 2).getValues(); // A:B
var mainSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("USERSHEET");
var mainRange = mainSheet.getRange(2, 23, mainSheet.getLastRow() - 1, 2); // W:X
var mainValues = mainRange.getValues();
for (var i = 0; i < mainValues.length; i++) {
for (var j = 0; j < queryValues.length; j++) {
if (mainValues[i][0] == queryValues[j][0]) {
mainValues[i][1] = queryValues[j][1];
}
}
}
mainRange.setValues(mainValues);
}
Note:
In this modification, the column "W" and "X" of USERSHEET is overwritten. If you want to changed this situation, please tell me.
If I misunderstood your question, I apologize.
Try this:
This compares Column W of main sheet to Column A of query sheet and puts value in column B of the query sheet into column X in main sheet in the same row as the match in column A. It could be a little faster by moving the setValue out of the loop but I didn't want to mess around over writing your other data. Also I have not tested this so there is always the possibility of needing corrections.
function match() {
var qss=SpreadsheetApp.openById('id');
var qsh=qss.getSheetByName('query');
var qrgA=qsh.getRange(2,1,qsh.getLastRow(),1);//colA
var qrgB=qsh.getRange(2,2,qsh.getLastRow(),1);//colB
var qvB=qrgB.getValues();
var qvA=qrgA.getValues();
var qvAf=qvA.map(function(r){return r[0]});//flatten array
var mss=SpreadsheetApp.getActive();
var msh=mss.getSheetByName('USERSHEET');
var mrg=msh.getRange(2,23,msh.getLastRow(),1);//colW
var mVa=mrg.getValues();
for(var i=0;i<mVa.length;i++) {
var index=qvAf.indexOf(mvA[i][0]);
if(index>-1) {
msh.getRange(i + 2,24).setValue(qvB[index][0]);//put matches in same row of columnX
}
}
}

How find a specific row based on a cell value and insert current date into a cell of that row

I have a sheet that contains hundreds of rows.
The first column includes Reference Number which is unique to each row.
I want to find the row whose Reference Number is a specific number, for example 301, and insert a timestamp (date and time) into the cell of that row which is under column M.
I have the following code, which isn't complete yet and won't work.
var spreadsheet = SpreadsheetApp.getActive();
var RefNumber = 301 //for example
var searchRange = spreadsheet.getSheetByName('Classes').getRange("A:A");
// get the values in an array
var values = searchRange.getValues();
// examine the values in the array
var i = [];
for (var y = 0; y < values.length; y++) {
if(values[y] == RefNumber){
i.push(y);
}
}
// I have no idea how to continue the code! Please include your suggestions here.
You want to search a number from column A, and put the timestamp to the column M with the same row which found the searched number. If my understanding is correct, how about this modification?
Modification points :
Values retrieved by getValues() is 2 dimensional array.
By using getLastRow(), the cost can be lower than that of "A:A".
When RefNumber is found from values, it puts a1Notation to i. By this, the timestamp can be put using getRangeList().
Modified script :
var spreadsheet = SpreadsheetApp.getActive();
var RefNumber = 301 //for example
var sheet = spreadsheet.getSheetByName('Classes'); // Modified
var searchRange = sheet.getRange("A1:A" + sheet.getLastRow()); // Modified
// get the values in an array
var values = searchRange.getValues();
// examine the values in the array
var i = [];
for (var y = 0; y < values.length; y++) {
if(values[y][0] == RefNumber){
i.push("M" + (y + 1)); // Modified
}
}
// I have no idea how to continue the code! Please include your suggestions here.
sheet.getRangeList(i).setValue(new Date()); // Added
References :
getValues()
getLastRow()
getRangeList(a1Notations)
If I misunderstand your question, I'm sorry. At that time, can you provide the sample input and output you want? I would like to modify the script.