I have 2 sheets that both have dates saved to columns. My current date is saved on both Sheet1 and Sheet2 with the same code:
curDate = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy");
I want to have one of my scripts compare the dates from Sheet1 to Sheet2
On Sheet1 I used a small script to set the current date and then used the drag function to set the previous and next dates in the column using the same formatting here:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Update Dates')
.addItem('Set Dates', 'setDatesUp')
.addToUi();
}
function setDatesUp(){
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet1");
var curDate = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy");
sheet1.getRange("A4").setValue(curDate);
}
Once the dates on Sheet1 are set up I use the following script to compare the dates from Sheet1 and Sheet2, this script also sets the date for Sheet2 because when this script is activated it's supposed to mark the current date in the corresponding box.
function onEdit() {
//Loops through all of Column C to determine which values are True and False
//Saves all True Values Row # in SavedValues
//Initialize Variables
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var sheet2 = ss.getSheetByName("Sheet2");
//Sheet 2 S2
var cValuesS2 = sheet2.getRange("C:C").getValues();
var dValuesS2 = sheet2.getRange("D:D").getValues();
var lastRowS2 = sheet2.getLastRow();
//Variables
var curDate;
var curVar;
//Loops through all S2 rows, stops at last row
for (var i = 0; i <= lastRowS2; i++){
//Checks checkboxes in S2C:C for True OR "Complete" and adds dates in corresponding D:D cells
if (cValuesS2[i] == "true" || cValuesS2[i] == "Complete") {
//If D:i is empty
if (dValuesS2[i] == "") {
//Sets current date
curDate = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy");
//Set current D:i cell (*+1 offset)
curVar = ("D" + (i + 1));
//Sets curVar value to curDate
sheet2.getRange(curVar).setValue(curDate);
}
//Checks checkboxes in S2C:C for False OR "Incomplete" and deletes dates in corresponding D:D cells
} else if (cValuesS2[i] == "false" || cValuesS2[i] == "Incomplete") {
//If D:i is NOT empty
if (dValuesS2[i] != "") {
//Set current D:i cell (*+1 offset)
curVar = ("D" + (i + 1));
//Sets curVar to ""
sheet2.getRange(curVar).setValue("");
}
}
}
updateS1();
}
Then finally I have my script to compare the dates from the 2 Sheets together.
function updateS1() {
//Initialize Variables
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var sheet1 = ss.getSheetByName("Sheet1");
var sheet2 = ss.getSheetByName("Sheet2");
//Sheet 1 S1
var aValuesS1 = sheet1.getRange("A:A").getValues();
var lastRowS1 = sheet1.getLastRow();
//Sheet 2 S2
var dValuesS2 = sheet2.getRange("D:D").getValues();
var lastRowS2 = sheet2.getLastRow();
//Variables
var curVar;
var curVar2;
//Loop through Sheet 1 until the bottom
//For each value in S1 A I use i
for (var i = 0; i <= lastRowS1; i++) {
//Loop through Sheet 2 until the bottom
//For each value in S2 D I use j
for (var j = 0; j <= lastRowS2; j++) {
//TODO: Compare dates from S1 A:i to S2 D:j
//If they are the same date the if statement will execute
}
}
}
I've already tried using the following
if (aValuesS1[i].toString() == dValuesS2[j].toString()) {
}
and
if (aValuesS1[i] == dValuesS2[j]) {
}
but neither option has worked. I've noticed that when I grab the "value" for the date in the Logger I get a lot of information that I don't want or need:
Thu Oct 30 2018 00:00:00 GMT-0400 (EDT)
instead of 8/30/18.
I think that this is the reason that I do not get "matching" values even if both of my boxes show the date formatted the same. I'm honestly stumped at how to solve this, so any help would be greatly appreciated.
So as it turns out, I actually can compare in my if statement with .toString() it just takes quite a while for my loop to execute in the Google Apps Script.
Related
I am very new to coding so I appreciate the community keeping this in mind. This script is limited to worksheet 1 and worksheet 2. When a line is edited the user email is recorded in the edited row column 41. I would also like to put a timestamp in column 42.
Bottom Line: I need for this to include a timestamp in column 42 AND exclude edits on the first (header) row.
function onEdit (e)
{
var ss = SpreadsheetApp.getActive()
var sheets = ["Worksheet","Worksheet2"]
var ws = e.range.getSheet()
if (sheets.indexOf(ws.getName()) != -1)
{
var row = e.range.getRow()
var rng = ws.getRange(row,41)
rng.setValue(e.user.getEmail())
function onEdit (e)
{
var ss = SpreadsheetApp.getActive()
var sheets = ["Worksheet","Worksheet2"]
var ws = e.range.getSheet()
if (sheets.indexOf(ws.getName()) != -1)
{
var row = e.range.getRow()
var rng = ws.getRange(row,41)
rng.setValue(e.user.getEmail())
var timezone = "GMT-4";
var timestamp_format = "yyyy-MM-dd HH:mm:ss";
var dateCol = headers[0].indexOf('Date');// create a column if its not exits
// note: actRng = the cell being updated
var actRng = event.source.getActiveRange();
var index = actRng.getRowIndex();
if (dateCol > -1 && index > 1) { // only timestamp if 'Date' header exists, but not in the header row itself!
var cell = sheet.getRange(index, dateCol + 1);
var date = Utilities.formatDate(new Date(), timezone, timestamp_format);
cell.setValue(date);
}
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Worksheet");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Worksheet2");
var valuesToCopy = sheetFrom.getRange(0, 41, sheetFrom.getLastRow(), 1).getValues();
//Paste to another sheet from first cell onwards
sheetTo.getRange(1,sheetTo.getLastColumn()-1,valuesToCopy.length,1).setValues(valuesToCopy);
}
}
This will add current time to column 42 and copy column 41 from worksheet one to worksheet2
I was able to figure it out, thank you for the help it pointed me in the right direction. The following works:
function onEdit (e)//get the range of the edited cell
{
var ss = SpreadsheetApp.getActive()
var sheets = ["Worksheet","Copy of Worksheet"]
var ws = e.range.getSheet()
if (sheets.indexOf(ws.getName()) != -1)
// Check if the edit was made in an applicable sheet
{
var row = e.range.getRow()
if(row != 1){
//Get the row of the edited cell
var rng = ws.getRange(row,41)
//Range = Row of edited cells and Column 41
rng.setValue(e.user.getEmail())
var rng = ws.getRange(row,42)
//Range = Row of edited cells and Column 42
rng.setValue(Utilities.formatDate(new Date(),"America/New_York","yyyy-MM-dd HH:mm"))
}}}
I have Google form which is linked to Form Response Sheet. I want to Move data from "Discrepancy Report - Outlet" to "Vaishali Nagar - DTR" Sheet on each day. The date is to be used from "Discrepancy Report - Outlet" sheet from Colum J.
The condition is that if I run the script today it should move data of Previous Day. For Ex. If I run script Today i.e. 31/10/2020 then It should only pick data of 30/10/2020. This process goes on each day. For running this script I will use Time Trigger.
I am using script which is mentioned below the Question. The problem I am facing it runs on the basis of today's date.
Link of Spreadsheet is:
https://docs.google.com/spreadsheets/d/1MhCdwFscPqskeeM2Hza-t2VXoVkI6sq3XRCNybMm4NM/edit?usp=sharing
function copyrange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Discrepancy Report - Outlet'); //source sheet
var testrange = sheet.getRange('J:J');
var testvalue = (testrange.setNumberFormat("#").getValues());
Logger.log(testvalue);
var ds = ss.getSheetByName('Vaishali Nagar - DTR'); //destination sheet
var data = [];
var j =[];
var today = new Date();
var yesterday = new Date(today); yesterday.setDate(yesterday.getDate()-1);
var today = Utilities.formatDate(new Date(), 'GMT-0', 'dd/MM/yyyy')
//Condition to check in J:J, if true, copy the same row to data array
for (i=0;i<testvalue.length;i++) {
if (testvalue[i] == today) {
data.push.apply(data,sheet.getRange(i+1,1,1,9).getValues());
//Copy matched ROW numbers to B
j.push(i);
}
}
//Copy data array to destination sheet
var start_row=ds.getRange('B7:B').getValues().filter(String).length +6; //calculate max row
ds.getRange(start_row+1,1,data.length,data[0].length).setValues(data);
}
Explanation:
The main issue is that your current code compares the dates with the date of today instead of yesterday.
However, your code can be further optimized. Instead of using a for loop to check if an element matches the date of today, you can use filter to get the data only for yesterday and then remove the lst column because you don't need it to be in the destination sheet:
yest_data = data.filter(r=>r[9]==yesterday).map(v=>v.slice(0,-1));
It is a good practice to check if the data has a length higher than 0, otherwise the script will drop an error when setting the values.
Solution:
function copyrange() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Discrepancy Report - Outlet'); //source sheet
const ds = ss.getSheetByName('Vaishali Nagar - DTR'); //destination sheet
const data = sheet.getRange('A2:J'+ sheet.getLastRow()).getDisplayValues();
var yesterday = new Date();
yesterday.setDate(yesterday.getDate()-1);
var yesterday = Utilities.formatDate(yesterday, ss.getSpreadsheetTimeZone(), 'dd/MM/yyyy');
yest_data = data.filter(r=>r[9]==yesterday).map(v=>v.slice(0,-1));
const start_row=ds.getRange('B7:B').getValues().filter(String).length +6; //calculate max row
if (yest_data.length>0){
ds.getRange(start_row+1,1,yest_data.length,yest_data[0].length).setValues(yest_data);
};
}
When checking date equality, you should use the date value or epoch time. Try this instead, which allows you to pass a specific date as your today value.
function test() {
var now = new Date('October 28, 2020');
var today = new Date(now.setHours(0, 0, 0, 0));
copyYesterday(today);
}
/**
* Copy yesterday's responses from Sheet1 to Sheet2.
* #param {Date} [today] - Optionally include a value to use as "today".
*/
function copyYesterday(today) {
if (today == null) { today = new Date(new Date().setHours(0, 0, 0, 0)); }
var yesterday = (new Date(today)).setDate(today.getDate() - 1);
var ss = SpreadsheetApp.getActive();
var sourceSheet = ss.getSheetByName('Sheet1');
var sourceData = sourceSheet.getDataRange().getValues();
var responses = [];
for (var rowIndex = 0; rowIndex < sourceData.length; rowIndex++) {
var row = sourceData[rowIndex];
var responseDate = row[0];
if (responseDate.valueOf() == yesterday) {
responses.push(row);
}
}
if (responses.length > 0) {
var destination = ss.getSheetByName('Sheet2');
destination.getRange(destination.getLastRow()+1, 1, responses.length, responses[0].length).setValues(responses);
}
}
function copyrange() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Discrepancy Report - Outlet'); //source sheet
var rg=sh.getRange(1,10,sh.getLastRow(),1);
var vA= rg.setNumberFormat("#").getValues();
var ds = ss.getSheetByName('Vaishali Nagar - DTR'); //destination sheet
var data=[];
var j=[];
var today = new Date().valueOf();
var yesterday = new Date(today.getFullYear(),today.getMonth(),today.getDate()-1).valueOf();
for (var i=0;i<vA.length;i++) {
var dt=new Date(vA[i][0]);
var dtv=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()).valueOf();
if (dtv==today) {
data.push(sheet.getRange(i+1,1,1,9).getValues());
}
}
ds.getRange(getColumnHeight(ds,2,ss)+1,1,data.length,data[0].length).setValues(data);
}
function getColumnHeight(col,sh,ss){
var ss=ss||SpreadsheetApp.getActive();
var sh=sh||ss.getActiveSheet();
var col=col||sh.getActiveCell().getColumn();
const rcA=sh.getRange(1,col,sh.getLastRow(),1).getValues().reverse()
let s=0;
for(let i=0;i<rcA.length;i++) {
if(rcA[i][0].toString().length==0) {
s++;
}else{
break;
}
}
return rcA.length-s;
}
I have a rota (a fixed order of rotation (as of persons or duties)) that I've already had help with this week. It's up & running as is, but for simpler reading I'd like to transpose it.
You can see the transposed sheet as I'd like it here
The current script is for the pre-transposed table.
It would search Column 0 for the date. If it was was 7 days away it would retrieve the name from Column 1 & match it with e-mail address in separate sheet etc.
What I'd like to do is instead have the Date in Row 0 & then subsequent names in Row 1 etc etc
I've tried various things. I've stepped through the code & can see what it's doing, & I've done some reading through about 2 dimensional arrays, but I can't seem to find a way of getting the code to work down through columns, instead of across the rows.
Here's the code:
function sendEmails() {
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss1.getSheetByName("Rota")
ss1.setActiveSheet(sh1);
var rotalink = "https://docs.google.com/spreadsheets/d/1LgzUWSAGA2kbpar8r5nosU1bSHF7nrtvtUiHS3nB_e8";
var sheet = SpreadsheetApp.getActiveSheet();
// Fetch the range
var dataRange = sheet.getRange("B3:G50")
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var today=new Date();
var timecell = new Date(row[0]);
var timediff = new Date();
var one_day=1000*60*60*24;
var daystogo = Math.ceil((timecell.getTime()-today.getTime())/(one_day));
if (daystogo==7) {//only e-mail people with one week to go. To change that alter the "7" to the number of days you want
var subject = "Rota reminder!";
var emailAddress = [];
var message;
message = "Hello \n\n"+
"You are down to help at Youth Café this week. \n\n" +
"Please see the below rota for your role \n\n" +
"If you have any questions or problems let us know at thameyouthcafe#gmail.com \n\n" +
"Remember, you can check the rota anytime by clicking on the link below: \n\n"+
rotalink
for (var x = 1; x < 5; x++) { // 5 because emails are till col4
// var emailAddress = []; // Start by collecting the non-blank emails in an array
if (getEmailFromName(row[x]) != "") {
emailAddress.push(getEmailFromName(row[x]))
}
}
emailAddress = emailAddress.join(); // Join the array to get a comma separated string
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
and here's the getEmailFromName function that matches with SKey (which I presume comes from the "i" variable in the first function?
function getEmailFromName(sKey) {
// to use this function, don’t put anything in the first column (A) or row (1).
// Put the name (i.e. the key, or what we’re looking for) in column B.
// Put what we want to return in column C.
var columnToSearch = 1; //column B
// Set the active sheet to our email lookup
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss1.getSheetByName("EmailContactList")
ss1.setActiveSheet(sh1);
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var line = -1;
for( var i = 0; i < data.length; i++ ) {
if( data[i][columnToSearch] == sKey ) {
line = i;
break;
}
}
if( line != -1 ) {
//do what you want with the data on "line"
return data[line][2]; //value on column C of the matched line
}
else {
return "";
// if criteria is not found
}
}
Try it this way:
function sendEmails() {
var ss1 = SpreadsheetApp.getActive();
var sh1 = ss1.getSheetByName("Rota")
ss1.setActiveSheet(sh1);
var rotalink = "https://docs.google.com/spreadsheets/d/1LgzUWSAGA2kbpar8r5nosU1bSHF7nrtvtUiHS3nB_e8";
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange("B3:G50")
var data = dataRange.getValues();
for (var i=0;i<dataRange.length;i++) {
var row = data[i];
var today=new Date();
var timecell = new Date(row[0]);
var timediff = new Date();
var one_day=1000*60*60*24;
var daystogo = Math.ceil((timecell.getTime()-today.getTime())/(one_day));
if (daystogo==7) {
var subject = "Rota reminder!";
var emailAddress = [];
var message = Utilities.formatString('Hello\n\nYou are down to help at Youth Café this week.\n\n Please see the below rota for your role \n\nIf you have any questions or problems let us know at thameyouthcafe#gmail.com \n\nRemember, you can check the rota anytime by clicking on the link below: \n\n%s',rotalink);
for (var x=1;x<5;x++) {
if(data[i][x]) {
emailAddress.push(data[i][x]);
}
}
MailApp.sendEmail(emailAddress.join(), subject, message);
}
}
}
Managed to solve it - thank you for your contributions. Turned out it was incredibly simple.
Just had to change this line:
var timecell = new Date(data[0])
to this:
var timecell = new Date(data[0][i])
so it iterates through the first row of each column.
im using this script to auto date Col 1, when I edit Col 6
function onEdit(e) {
var colToWatch = 6, colToStamp = 1;
var timezone = "GMT-6";
var timestamp_format = "MM-dd-yyyy"; // Timestamp Format.
if (e.range.columnStart !== colToWatch) return;
if (e.source
.getActiveSheet()
.getRange(e.range.rowStart, colToStamp)
.getValue()) {
return;
}
var date = Utilities.formatDate(new Date(), timezone, timestamp_format);
e.source.getActiveSheet()
.getRange(e.range.rowStart, colToStamp)
.setValue(date);
}
is working perfect
What I have to do to prevent this onEdit to don't run on some tabs ?
Thanks !
You can define an array of tabs to exclude then check if current sheet is one among the excludes. Add this code after line 4.
var name = e.source.getActiveSheet().getName();
// enter names of the sheets to exclude, ['Sheet1', 'Sheet2'] etc
var excludes = [];
if (excludes.indexOf(name) != -1) return;
How can I change the background color for only the rows that were (true) as per function checkDate(row) on the originating sheet "Pasco"? Is this possible?
A little bit about the script:
A date range is inputted through function getDateRange(), all rows in sheet "Pasco" is checked for if they meet that date range through function checkDate(row). If it does meet the date range (true), function filterRows() essentially filters the rows from "Pasco" sheet, and moves them over to another sheet "Copy of Pasco".
Another way of asking my question, how can I get a range of all the rows that were "true" in sheet "Pasco". If "Pasco" wasn't sorted by date, this could mean multiple ranges, right? Once I have a range I'd be able to change background easy.
If you are to test the script, please create two sheets, 'Pasco' and 'Copy of Pasco'. In 'Pasco' Starting from row 2, place some dates down column I (column 8). To see the filtering in action. 'Copy of Pasco' will be deleted/created on each run.
Thank you for your time =)
var globalStartDate;
var globalEndDate;
function getDateRange(){
var startui = SpreadsheetApp.getUi();
var startprompt = startui.prompt('Start Date', 'Enter a date in m/d/y format', startui.ButtonSet.OK_CANCEL);
var startdate = new Date(startprompt.getResponseText());
var startdatemilliseconds = startdate.getTime();
Logger.log(startdate);
Logger.log(startdatemilliseconds);
globalStartDate = startdatemilliseconds;
var endui = SpreadsheetApp.getUi();
var endprompt = endui.prompt('End Date', 'Enter a date in m/d/y format', endui.ButtonSet.OK_CANCEL);
var enddate = new Date(endprompt.getResponseText());
var enddatemilliseconds = enddate.getTime();
Logger.log(enddate);
Logger.log(enddatemilliseconds);
globalEndDate = enddatemilliseconds;
}
function checkDate(row) {
Logger.log(row[8].getTime() <= globalEndDate && row[8].getTime() >= globalStartDate);
return (row[8].getTime() <= globalEndDate && row[8].getTime() >= globalStartDate); // Check column H
}
function filterRows() {
var Spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = Spreadsheet.getSheetByName('Pasco');
var sheetdelete = Spreadsheet.getSheetByName('Copy of Pasco');
Spreadsheet.deleteSheet(sheetdelete);
Spreadsheet.setActiveSheet(sheet1);
Spreadsheet.duplicateActiveSheet();
var headers = 1; // # rows to skip
var sheet2 = Spreadsheet.getSheetByName('Copy of Pasco');
var range = sheet1.getDataRange();
var data = range.getValues();
var headerData = data.splice(0,headers); // Skip header rows
getDateRange();
var filteredData = data.filter( checkDate );
var outputData = headerData.concat(filteredData); // Put headers back
Logger.log(filteredData)
sheet2.clearContents(); // Clear content, keep format
// Save filtered values
sheet2.getRange(1, 1, outputData.length, outputData[0].length).setValues(outputData);
}
Sorry I don't have time to read through your code and give you a complete answer but you could just add a loop to go through the sheet and set the background colour of each row with 'true'.
In my script below I assume 'true' is in column A.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var data = sheet.getRange(1, 1, sheet.getLastRow()).getValues();
var lastCol = sheet.getMaxColumns();
for (var i = 0; i < data.length; i ++){
if(data[i][0] == true){
sheet.getRange(i + 1, 1, 1, lastCol).setBackground('Yellow');
}
}
}
EDIT
Insert this code after you call getDateRange() in the filter rows function.
var lastCol = sheet1.getMaxColumns();
for(var i = headers; i < data.length ; i++){
if(data[i][8].getTime() <= globalEndDate && data[i][8].getTime() >= globalStartDate){
sheet1.getRange(i, 1, 1, lastCol).setBackground('Yellow');
}
}
Your filter rows function should now look like this:
function filterRows() {
var Spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = Spreadsheet.getSheetByName('Pasco');
var sheetdelete = Spreadsheet.getSheetByName('Copy of Pasco');
Spreadsheet.deleteSheet(sheetdelete);
Spreadsheet.setActiveSheet(sheet1);
Spreadsheet.duplicateActiveSheet();
var headers = 1; // # rows to skip
var sheet2 = Spreadsheet.getSheetByName('Copy of Pasco');
var range = sheet1.getDataRange();
var data = range.getValues();
var headerData = data.splice(0,headers); // Skip header rows
getDateRange();
var lastCol = sheet1.getMaxColumns();
for(var i = headers; i < data.length ; i++){
if(data[i][8].getTime() <= globalEndDate && data[i][8].getTime() >= globalStartDate){
sheet1.getRange(i + headers, 1, 1, lastCol).setBackground('Yellow');
}
}
var filteredData = data.filter( checkDate );
var outputData = headerData.concat(filteredData); // Put headers back
Logger.log(filteredData)
sheet2.clearContents(); // Clear content, keep format
// Save filtered values
sheet2.getRange(1, 1, outputData.length, outputData[0].length).setValues(outputData);
}