Google Script does not trigger as expected given row conditions - javascript

I have the 2 columns in my table schema:
Column D= Date, i.e. 20180611 [yyymmdd]
Column F= Continuous Value, i.e. 0.1, 0.6, -0.3 etc.
This is what I want to happen:
Check in column D for yesterday's date. Then, take in the corresponding row, and check if column F is greater than 0.5 (for yesterday's date). If TRUE, then send an email alert.
This is the script I have but it does not trigger for some reason. What is wrong with it?
function readCell() {
var sheet = SpreadsheetApp.getActive().getSheetByName('test');
var dates = sheet.getRange('D1:D').getValues();
var date = null;
var dateRow = 0;
var dateCount = dates.length;
var yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
var yesterdayString = yesterday.toDateString();
for (dateRow; dateRow < dateCount; ++dateCount) {
try {
date = dates[dateRow].toDateString();
if (date === yesterdayString) {
++dateRow;
// To account for zero-based array
break;
}
} catch (error) {
Logger.log(error);
}
}
var value = sheet.getRange('F' + dateRow).getValue();
if (value >= 0.5) {
var result = ('Alert found on: ' + date);
MailApp.sendEmail('blabla#gmail.com', 'Alert', result);
}
};
Here is the data

The problem could be due to the use of an open reference D2:D to get values and then use dates.length to set the number of iterations on the for loop because it could be a number too large.
One "quick and dirty" way that could solve the above issue is to replace
var dateCount = dates.length;
by
var dateCount = sheet.getDataRange().getValues().length;

Related

Return index of matched array not working - Javascript Google Apps Script

I have a spreadsheet and row #1 has dates in each cell going across
I want to return the column number whenever that column matches today's date. First header starts in cell B1.
I am using the following and I can get it to work, but when instead I do 'return i', it always returns '0'.
function getColumnIndex() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var lastColumn = sheet.getLastColumn();
var data = sheet.getRange(1,2,1,lastColumn).getValues(); //create an array of data from row 1
for (var i = 0; i <= data.length; i++) {
var dateToday = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy")
if (data[i] == dateToday) {break};
{
return i;
}
}
}
Now if I switch the last line 'return i' to 'return dateToday' the function will work and it will return the correct date so I know it's matching properly (and if I change row cells to other values it will return those values if it matches). I just can't get it to spit out the index number when I put 'return i'.
Issues / Explanation:
var data = sheet.getRange(1,2,1,lastColumn).getValues(); returns a 2D array.
As a result, data[i] returns a 1D array which is actually referring to the row. To solve this issue, flatten the array to convert it to 1D:
var data = sheet.getRange(1,2,1,lastColumn).getDisplayValues().flat();
Your if condition is executed at the first iteration i=0 because you put a semicolon ; right after it. Also, break is not needed because nothing will be executed after the return statement:
Replace:
if (data[i] == dateToday) {break};
{
return i;
}
with
if (data[i] == dateToday)
{
return i;
}
When you are working with date comparisons, you need to use getDisplayValues() to be sure that you are comparing the the displayed values
and not the value of the date.
Solution:
function getColumnIndex() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var lastColumn = sheet.getLastColumn();
var data = sheet.getRange(1,2,1,lastColumn).getDisplayValues().flat(); //create an array of data from row 1
for (var i = 0; i <= data.length; i++) {
var dateToday = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy")
if (data[i] == dateToday)
{
return i;
// return i+2; // if you want to get the column number instead.
}
}
}
Keep in mind, i refers to the position of the array. In JavaScript, the indexes in the arrays start from 0. Also, your data starts from the second column. If you want your script to return the number of column, then change return i to return i+2.
function getColumnIndexForToday() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getActiveSheet();
const shsc=2;
const offset=0;//0 if you want the index from column B 1 if you want the index from ColumnA
const data=sh.getRange(1,shsc,1,sh.getLastColumn()-shsc+1).getDisplayValues()[0];//assuming format is "MM/dd/yyyy"
var dObj={};
data.forEach(function(h,i){dObj[h]=i+offset;});//You really can just do this once and then use it repeatedly
var dateToday = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy")
return dObj[Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"MM/dd/yyyy")];
}

Creating Sorted Rows with JavaScript

At my wits end, I think I may be going code blind but can't for the life of me figure out what is causing the issue.
The desired outcome is for there to be only one date per row in the data array and as many flights for each date.
This works for the first item, but not any of the others, ending up with duplicate dates.
Where am I going wrong?
Desired data:
example in coming data 5 objects, only two dates.
["2020-02-20", "LGW"]
["2020-02-20", "LTN"]
["2020-02-20", "LHR"]
["2020-02-26", "LTN"]
["2020-02-26", "LHR"]
an array of two objects (one for each date), with the flights an array by date in the respective date object.
data = [ ["2020-02-20", ["LGW","LTN","LHR"]],
["2020-02-26", ["LTN","LHR"]]
]
Code shown below with comments:
function getRows(alternatives) {
var data = [];
for (var i = 0; alternatives.length > i; i++) {
var tmp = new Date(parseInt(alternatives[i].substring(0, 10)) * 1000);
var month = (tmp.getMonth() + 1);
var date = tmp.getFullYear() + "-" + (month < 10 ? "0" + month : month) + "-" + tmp.getDate();
var airport = alternatives[i].slice(11, 14);
var rowData = {
date: date,
flights: []
};
// if data has objects, check to see if the date is in any of the objects, if it isn't then add rowData to data
if (data.length > 0) {
for (var j = 0; data.length > j; j++) {
if (data[j].date === rowData.date) {
//if there are no flights, add the airport, if there are, is the airport already there, if not, add it
if (data[j].flights.length > 0 || !data[j].flights.includes(airport)) {
data[j].flights.push(airport);
}
}
else {
data.push(rowData);
continue;
}
}
}
else {
rowData.flights.push(airport);
data.push(rowData);
}
}
// not working, dupe dates are appearing in the rows
return data;
}
You can use reduce, create the object which has date as key with value consisting of array with date and array of flights. Check if key already exist, if present then only push in the flights array.
const input = [["2020-02-20", "LGW"],
["2020-02-20", "LTN"],
["2020-02-20", "LHR"],
["2020-02-26", "LTN"],
["2020-02-26", "LHR"]];
const output = Object.values(input.reduce((accu, [date, flight]) => {
if(!accu[date]) {
accu[date] = [date, [flight]];
} else {
accu[date][1].push(flight);
}
return accu;
}, {}));
console.log(output);

Trigger email alert based on last row's condition NOT WORKING

I attempted to build a script but there are some issues. The table format are 2 columns which are date and values. These are the needs:
IDEAL STATE
Grab the last filled row (today's date) in the Google Sheets called "test".
Check in that row if the value in column F is greater than 0.5.
If it greater than 0.5, then trigger an email.
In email body, it should state "Results found on [date]."
This was my starting point but it does not produce what I want. These are the issues:
CURRENT STATE
1.The script grabs every row in which column F was greater than 0.5 in the past. I only want to check for today (which would be the last row). It should not look through everything in the past.
2.The email body states: Result found on [row number]". This makes no sense. I want the date to show, not the row number.
This is the current code. Please help.
function readCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("test");
var values = sheet.getRange("F3:F").getValues(); //this has the values
var date = sheet.getRange("D3:D").getValues(); // this has the date
var results = [];
for(var i=0;i<values.length;i++){
if(values[i]>=0.5)
{
results.push("Result found on:" +(i+3));
}
}
MailApp.sendEmail('blabla#gmail.com', 'Alert', results.join("\n"));
};
Last Row in this context is Row 217, not 218, assuming sheet.getLastRow() would ignore #DIV/o! values. See screenshot for this.
LATEST UPDATE
The current Error is related "toDateString". I think it may be related that my Google Sheet is one day behind. So, it today is Jan 10, the last row in my Google Sheet is Jan 9th. I think that is why the error happens. Can you confirm? In that case, how do I change it to today-1 day?
See below.
Here's how you can check the last row:
function readCell() {
var sheet = SpreadsheetApp.getActive().getSheetByName('test');
var lastRow = sheet.getLastRow();
var value = sheet.getRange('F' + lastRow).getValue();
var date = sheet.getRange('D' + lastRow).getValue();
if (value >= 0.5) {
var result = 'Result found on: ' + date;
MailApp.sendEmail('blabla#gmail.com', 'Alert', result);
}
};
After seeing your data, I think the code below would suit you better.
function readCell() {
var sheet = SpreadsheetApp.getActive().getSheetByName('test');
var dates = sheet.getRange('D1:D').getValues();
var date = null;
var dateRow = 0;
var dateCount = dates.length;
var yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
var yesterdayString = yesterday.toDateString();
for (dateRow; dateRow < dateCount; ++dateCount) {
date = dates[dateRow];
if (date instanceof Date) {
date = date.toDateString();
if (date === yesterdayString) {
++dateRow;
// To account for zero-based array
break;
}
}
}
var value = sheet.getRange('F' + dateRow).getValue();
if (value >= 0.5) {
var result = 'Result found on: ' + date;
MailApp.sendEmail('blabla#gmail.com', 'Alert', result);
}
};

calculating with dates and for loop

I am trying to make a for loop with dates but it isn't going as planned.
What I am trying to do is input startdate in cell A1 and enddate in cell B1.
Then a for loop which runs: if startdate is less then enddate, put date in cell A2 and proceed to next column. The only thing I am stuck with is how to add one day to a date.
See code below:
function test()
{
var app = SpreadsheetApp;
var ss = app.getActive();
var sheet = ss.getActiveSheet();
var start = sheet.getRange("A1").getValue();
var end = sheet.getRange("B1").getValue();
var day = 3600*24*1000;
var diff = Math.floor((end-start)/day);
var column = 1;
for(var i=0;i<=diff;i++)
{
sheet.getRange(2, column).setValue(start+i*day);
column++;
}
}
Can someone explain to me why it doesn't work.
Thanks a lot
Assuming start is a Date, then your issue is:
start + i * day
Where the + operator is used, the left operand (the Date) is an object so is reduced to a primitive with hint string, so the + is treated as string concatenation, not a number.
You can try:
+start + i * day
to coerce start to a number, but then the result will be a number, not a Date. Also it doesn't deal with the length of day issue. I suggest you first copy the start date so you don't modify it, then increment the copy per the duplicate:
function test() {
var app = SpreadsheetApp;
var ss = app.getActive();
var sheet = ss.getActiveSheet();
var start = sheet.getRange("A1").getValue();
start = new Date(+start);
var end = sheet.getRange("B1").getValue();
var column = 1;
for (var i=0; start <= end; i++) {
sheet.getRange(2, column++).setValue(new Date(start.setDate(start.getDate() + 1)));
}
}

Get the next highest date value after excluding values from an array

I have a myDate variable with the value 18-Nov-2013.Each day its value is being changed.Tommorow this myDate variable will have the value 19-Nov-2013.I have a list of values that i have mapped into a single array named exclude which contains some dates that are to be excluded ,now it has values ["20-Nov-2013",21-Nov-2013", "23-Nov-2010"] .How could i filter my value from the list of values from the exclude array.I need the next highest value from the array.So here i need the value 22-Nov-2013 after tommorrows date.Could someone help me with this.
var excluded = ["30-Nov-2013","01-Dec-2013","02-Dec-2013"];
var myDate = "29-Nov-2013";
var month = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var current = new Date(myDate);
while(true){
current = new Date((current.getDate()+1<10? "0"+(current.getDate()+1):(current.getDate()+1))+ "-" + month[current.getMonth()] + "-" + current.getFullYear());
var checkDate = (current.getDate()<10? "0"+(current.getDate()):(current.getDate()))+ "-" + month[current.getMonth()] + "-" + current.getFullYear();//this is necessary for when the +1 on day of month passes the month barrier
if(-1 == excluded.indexOf(checkDate))
break;
}
alert(checkDate);
I don't know if this is the best approach, or if is the best algorithm, but you may try this:
var myDate = ["17-Nov-2013", "18-Nov-2013"];
var excluded = ["20-Nov-2013", "21-Nov-2013", "23-Nov-2013"];
var months = {"Nov": 10}; // Add others months "Jan": 1, "Fev": 2 etc...
function findExcluded(date)
{
for (var i = 0; i < excluded.length; i++)
{
if (excluded[i] === date)
{
return true;
}
}
return false;
}
function nextDate()
{
var last = myDate[(myDate.length - 1)];
var s = last.split("-");
var d = new Date(s[2], months[s[1]], s[0]);
var next = new Date(d);
var chkDate = "";
do
{
next.setDate(next.getDate() + 1);
chkDate = next.getDate() + "-" + findMonth(next.getMonth()) + "-" + next.getFullYear();
} while(findExcluded(chkDate));
return chkDate;
}
function findMonth(m)
{
var i = 10; // When you fill all months on 'months' array, this variable should start at '0' in order to loop to works.
for (var month in months)
{
if (i == m)
{
return month;
}
i++;
}
}
var nd = nextDate();
alert(nd);
See it woring here.
No code ? Well here will be my method:
1.Get next date for mydate. Say that is var nextDate.
2.Check whether that date exist in the array.
3.If exists add one more day to nextDate. Again check in the array.
4.Do it until you get a date which is not present in your exclude array
For checking whether it exists in the array you can use arrValues.indexOf(nextDateInProperFormat) > -1

Categories

Resources