Copying certain rows to duplicated sheet using macro - if-statement

I've got a google sheet which I'm using as a monthly handover sheet with a to-do list at the bottom.
The to-do list has a checkbox then a couple of columns of information (a date, the task and who it's assigned to)
To-Do List
I've created a button that runs a macro to duplicate the sheet to create a copied sheet at the end of the month.
I'd like to copy over any tasks in the to-do list which haven't been 'ticked' as completed but I'm not very good at logic in google apps script.
Could anyone help me write the if statement to either copy unticked rows to the duplicated sheet or do duplicate all rows then delete the ticked ones.
Thanks, Joe
UPDATE: after playing around for a couple of hours, this is what I've got:
function ToDoCopy() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var duplicateSheet = ss.getSheetByName("JUNE");
var originalSheet = ss.getSheetByName("MAY");
var lastRow = originalSheet.getLastRow() + 1;
for(var j = 24; j < lastRow; j++)
{
if(originalSheet.getRange(j,2).getValue() ==false && originalSheet.getRange(j,3).getValue() != 4)
{
var nextRow = duplicateSheet.getLastRow() +1;
var getCopyRange = originalSheet.getRange('B' + j + ':P' + j);
getCopyRange.copyTo(duplicateSheet.getRange(nextRow, 2));
}
}
}
It's almost working but it adds the copied items underneath the to-do table on the new sheet rather than adding them to the top. I can't work out how to fix this!
(p.s. the && originalSheet.getRange(j,3).getValue() != 4 part is a hidden row which I use for auto-sorting the table, '4' means it's empty basically.

function ToDoCopy() {
const ss = SpreadsheetApp.getActive();
const ssh = ss.getSheetByName("MAY");//source sheet
const sshsr = 24;//start row
const srg = ssh.getRange(sshsr, 1, ssh.getLastRow() - sshsr + 1, ssh.getLastColumn());
const svs = srg.getDisplayValues();//useful for me when using checkboxes
const dsh = ss.getSheetByName("JUNE");//destination sheet
if (dsh.getLastRow() - sshsr + 1 > 0) {
dsh.getRange(sshsr, 1, dsh.getLastRow() - sshsr + 1, dsh.getLastColumn()).clearContent().setDataValidation(null);//Clear destination range if not already cleared
}
let a = 0;//row add counter
svs.forEach((r, i) => {
if (r[1] == 'FALSE' && r[2] != 4) {
ssh.getRange(sshsr + i, 1, 1, ssh.getLastColumn()).copyTo(dsh.getRange(sshsr + a++, 1));
}
});
}

Related

If any row in range (G11:G25) contains boolean (true) then run function, else msgBox

The function I'm running (clearRowContents) in sheet 'Section 2' will clear contents and validation for any checked item (col H) in a list as well as the checkbox itself (col G). The remaining unchecked boxes and list items will then be sorted to clear any blank rows just created by the clearRowContents function. This functions works as tested.
However, if no item is checked (col G == false) and the "clear" button is pressed, how can I have a message pop up letting the user know that they must first check the box next to the item and then press the button to clear its contents from the list? I'm trying to figure out how to write the script for the clearItemMessage function.
Also, for script writing purposes, this sheet will be duplicated many times to create various validation menus for different topics... each sheet will be a different "chapter" in a manual with its own unique set of drop downs (in a MASTER DROPDOWN tab).
link to sheet: https://docs.google.com/spreadsheets/d/1ZdlJdhA0ZJOIwLA9dw5-y5v1FyLfRSywjmQ543EwMFQ/edit?usp=sharing
code:
function clearItemMessage(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var checkboxRange = ss.getRangeList("$G$11:$G$25").getValues();
if (checkboxRange == true){
clearRowContents (col);
} else (Browser.msgBox("To delete items, select the box next to the items and then press the delete button."));
}
function clearRowContents (col){ // col is the index of the column to check for checkbox being true
var col = 7; //col G
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = ss.getDataRange().getValues();
//Format font & size
var sheetFont = ss.getRange("A:Z");
var boxFont = ss.getRange("$G$11:$G$25");
var listFont = ss.getRange("$H$11:$H$25");
sheetFont.setFontFamily("Montserrat");
boxFont.setFontSize(8)
.setFontColor("#434343")
.setBackground("#ffffff");
listFont.setFontSize(12)
.setFontColor("#434343")
.setBackground("#ffffff");
//clear 'true' data validations
var deleteRanges = data.reduce(function(ar, e, i) {
if (e[col - 1] === true) {
return ar.concat(["H" + (i + 1), "G" + (i + 1)]);
}
return ar;
}, []);
if (deleteRanges.length > 0) {
ss.getRangeList(deleteRanges).clearContent().clearDataValidations();
}
//sort based on checkbox value
var range = ss.getRange("$G$11:$H$25");
range.sort({column: 7, ascending: false});
}
In your situation, how about modifying clearItemMessage() as follows?
Modified script:
function clearItemMessage(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var checkboxes = ss.getRange("$G$11:$G$25").getValues();
if (checkboxes.filter(([g]) => g === true).length > 0){ // or if (checkboxes.some(([g]) => g === true)) {
clearRowContents();
} else {
Browser.msgBox("To delete items, select the box next to the items and then press the delete button.");
}
}
From your question, I understood your clearRowContents works. So I proposed to modify clearItemMessage.
In your clearRowContents, var col = 7 is used. So I think that function clearRowContents (col){ can be modified to function clearRowContents (){.
Reference:
filter()

Google Script - Removing Else statement properly so it skips/ignores

I have a checklist, column C indicates which test is automated by "enabled" or "disabled" written in the cells.
Further down the row is a column for the the Pass / Empty column for each test.
I have code that looks for if Enabled in column C, in X column on that row, mark as Pass automatically (or 'P' in my case).
The problem: If C column contains "Disabled" but also a Pass, when I run the script it replaces that Pass with an empty cell. How can I change the else statement to just ignore that cell and leave whatever is in it for anything but Enabled condition is met
function autoPassPC() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Aug 2020');
// What to put in the test result
var values1 = "P";
// Where to look for Auto:
var values2 = sheet.getRange("C10:C15" + sheet.getLastRow()).getValues();
// Keyword to look for in Auto: column
var putValues = [];
for (var i = 0; i < values2.length; i++) {
if (values2[i][0] === "Enabled") {
putValues.push([values1]);
} else {
putValues.push([""]);
}
}
// Put value1 inside row, column# for test result
sheet.getRange(10, 25, putValues.length, 1).setValues(putValues);
}
Basically how do I get rid of
} else {
putValues.push([""]);
}
properly? Just deleting this causes the script to put 'P' on every single row. Just want it to ignore the cells instead.
Thanks!
Removing the else statement will actually skip the row, but as soon as you skip a row, all following values in putValues will be on the wrong row. Instead of "skipping", try pushing the already existing value into putValues.
function autoPassPC() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Aug 2020');
// What to put in the test result
var values1 = "P";
// Where to look for Auto:
var enabledDisabled = sheet.getRange("C10:C15" + sheet.getLastRow()).getValues();
var testResultsRange = sheet.getRange("X10:X15" + sheet.getLastRow());
var testResults = testResultsRange.getValues();
// Keyword to look for in Auto: column
var putValues = [];
for (var i = 0; i < enabledDisabled.length; i++) {
if (enabledDisabled[i][0] === "Enabled") {
putValues.push([values1]);
} else {
putValues.push([testResults[i][0]]); // Push the existing cell value
}
}
// Put value1 inside row, column# for test result
testResultsRange.setValues(putValues);
}
You could also try getting the entire table at once thus having only one getValues() call.

Spreadsheet Script, match with wildcard and different cases

I am having trouble executing the Spreadsheet script below.
I think there are two mistakes but I do not know how to fix.
Could anyone help it to fix it?
1:Wildcard
if(original_date=='....-..-..')
2:if synteax
if(original_date=='....-..-..')
{condition="matched"}
Detail
On the spreadsheet, there are two columns.
The first columns have dates in a format as YYYY-MM-DD such as 2020-04-21.
But sometimes, they have different formats such as 04/21/2020.
The second columns are empty.
Only when the first column cell has the "YYYY-MM-DD" format, I want to copy the cell into the second cell in the second column.
*They have 10 rows.
Here is the script.
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1')
for(let i=1; i<=10; i++)
{
original_date_range = sheet.getRange(i, 1);
original_date = original_date_range.getValue();
cleaned_date_range = sheet.getRange(i, 2);
var condition = "";
if(original_date=='....-..-..')
{condition="matched"}
switch(condition)
{
case "matched":
cleaned_date_range.setValue(original_date);
case "":
cleaned_date_range.setValue("");
break;
}
}
I found the solution by myself.
1.Wildcard
The date "2020-04-25" can be written as /\d{4}.\d{2}.\d{2}/
2.If syntax
(Wildcard).test(string to check) is going to give you true/false output
In summary,
if you want to execute different tasks through the verification of date format such as 2020-04-25,
var sheet1 = SpreadsheetApp.getActive().getSheetByName('Sheet1')
var limit = sheet1.getLastRow()
for(let i=4; i<=10; i++) //You can add more rows if you have
{
var orignal_date_cell = sheet1.getRange(i, 1);
var orignal_date_cell_value = orignal_date_cell.getValue();
var target_date_cell = sheet1.getRange(i,2);
if ((/\d{4}.\d{2}.\d{2}/).test(orignal_date_cell_value))
{
target_date_cell.setValue(orignal_date_cell_value);
}
}

Gmail App search criteria

I have the following search criteria working very well in Gmail:
user#domain from:/mail delivery/ || /postmaster/ ||/Undeliverable/
I am trying to write Goole Apps code to return the same results. Here is the code:
var thread=GmailApp.search("user#domain from:/mail delivery/ || /postmaster/ ||/Undeliverable/ ");
I am getting different results. I am new to both Regex and Google Apps.
Try Amit Agarwal's tutorial on Gmail Search with Google Apps Script which includes Using Regular Expressions to Find Anything in your Gmail Mailbox:
function Search() {
var sheet = SpreadsheetApp.getActiveSheet();
var row = 2;
// Clear existing search results
sheet.getRange(2, 1, sheet.getMaxRows() - 1, 4).clearContent();
// Which Gmail Label should be searched?
var label = sheet.getRange("F3").getValue();
// Get the Regular Expression Search Pattern
var pattern = sheet.getRange("F4").getValue();
// Retrieve all threads of the specified label
var threads = GmailApp.search("in:" + label);
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var m = 0; m < messages.length; m++) {
var msg = messages[m].getBody();
// Does the message content match the search pattern?
if (msg.search(pattern) !== -1) {
// Format and print the date of the matching message
sheet.getRange(row,1).setValue(
Utilities.formatDate(messages[m].getDate(),"GMT","yyyy-MM-dd"));
// Print the sender's name and email address
sheet.getRange(row,2).setValue(messages[m].getFrom());
// Print the message subject
sheet.getRange(row,3).setValue(messages[m].getSubject());
// Print the unique URL of the Gmail message
var id = "https://mail.google.com/mail/u/0/#all/"
+ messages[m].getId();
sheet.getRange(row,4).setFormula(
'=hyperlink("' + id + '", "View")');
// Move to the next row
row++;
}
}
}
}

google-apps-script multiple criteria writing over headers

I have taken a bit of script from Serge which is great (original link here. I have added in a second criteria to exclude certain rows and it works great except, if there is not header in the sheet being copied to, it will not work (error: "The coordinates or dimensions of the range are invalid.") and if I enter a header or some other data, it overwrites it. Can anyone assist please? I have also found that is there is no match to the criteria I get following message "TypeError: Cannot read property "length" from undefined."
Also, what change would I need to make to change the cell 'dataSheetLog[i][12]' to the status variable, i.e. "COPIED" after I have copied it across. I have tried writing a setValue line but it is obviously the wrong instruction for that syntax.
Code is:
{
var Spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheetLog = Spreadsheet.getSheetByName("LOG");
var sheetMaint = Spreadsheet.getSheetByName("MAINTENANCE");
var Alast = sheetLog.getLastRow();
var criteria = "08 - Maintenance"
var status = "COPIED"
var dataSheetLog = sheetLog.getRange(2,1,Alast,sheetLog.getLastColumn()).getValues();
var outData = [];
for (var i in dataSheetLog) {
if (dataSheetLog[i][2]==criteria && dataSheetLog[i][12]!=status){
outData.push(dataSheetLog[i]);
}
}
sheetMaint.getRange(sheetMaint.getLastRow(),1,outData.length,outData[0].length).setValues(outData);
}
In:
sheetMaint.getRange(sheetMaint.getLastRow(),1,outData.length,outData[0].length).setValues(outData);
getLastRow() refers to the last occupied row and should be ,getLastRow() + 1,to keep from overwriting your headers and other problems.
Edited:
{
var Spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheetLog = Spreadsheet.getSheetByName("LOG");
var sheetMaint = Spreadsheet.getSheetByName("MAINTENANCE");
var Alast = sheetLog.getLastRow(); // Log
var criteria = "08 - Maintenance"
var status = "COPIED"
var dataSheetLog = sheetLog.getRange(2,1,Alast,sheetLog.getLastColumn()).getValues(); //Log
var dataSheetLogStatusRange = sheetLog.getRange(2,13,Alast,1); //Log
var dataSheetLogStatus = dataSheetLogStatusRange.getValues(); //Log
var outData = [];
for (var i =0; i < dataSheetLog.length; i++) {
if (dataSheetLog[i][2]==criteria && dataSheetLog[i][12]!=status){
outData.push(dataSheetLog[i]);
dataSheetLogStatus[i][0] = "COPIED";
}
}
if(outData.length > 0) {
sheetMaint.getRange(sheetMaint.getLastRow() + 1,1,outData.length,outData[0].length).setValues(outData);
dataSheetLogStatusRange.setValues(dataSheetLogStatus);
}
}
}
what change would I need to make to change the cell
'dataSheetLog[i][12]' to the status variable, i.e. "COPIED" after I
have copied it across.
You were trying to update the value in the array that was extracted from the sheet and not the sheet itself. As arrays are zero based and spreadsheets are not, to translate, +1 must be added to array row and column indices. I am assuming status is in column M of your sheet.