Comparing '==' in an IF statement is not working Google Sheets script - javascript

The objective is to compare ColA in the Orders sheet with ColF in the Ordered Items sheet, if they match grab the email from ColB.
The script outputs no errors, but it doesn't match the highlighted cells in either sheet.
(note: the items are automatically added by an app, so the formatting of the cells are default and need to keep it that way as I'm using the last 6 digits as the order reff eg; 49.263Z)
Orders sheet
Ordered Items sheet
function getEmailFromOrderedItemToOrders(){
var orders = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Orders');
var lr = getLastRow(orders, "A1:G");
Logger.log(lr); //LastRow index
//Get last 'OrderID (Paid at)' value from 'Orders' sheet
var orderIdCol = 1;
var orderId = orders.getRange(lr, orderIdCol).getValue();
Logger.log(orderId); //LastRow 'orderId' value
//Match 'orderId' to 'orderId' in 'Ordered Items' and return col 1
var items = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ordered Items');
var itemsData = items.getDataRange().getValues();
Logger.log(itemsData[0][1]); //'Purchase Email' col
Logger.log(itemsData[0][5]); //'Paid at' col
for(var i = 0; i<itemsData.length;i++){
if(itemsData[i][5] == orderId){ //Issue here: not comparing values as a match
var email = itemsData[i][1];
Logger.log(email); //Does not print
return i+1;
}
}
}

In javascript when you compare two new Date, you will be getting the value as false.
Reason is when you have two dates, its basically two different objects. So One object is not equal to another object when you use == or ===. So the simple and better way is converting the date to a number using .getTime() that will return how many milliseconds passed from that date and 00:00:00 of Jan 1, 1970.
Sample code snippet
console.log(new Date() === new Date())
console.log(new Date().getTime() === new Date().getTime())

This can be simply solved by using a Sheets Formula which. What you would do is:
=INDEX( 'Ordered Items!B:B', MATCH(A2, 'Ordered Items!:F:F,0) )
The formula basically says:
Return the Email Column
Find the Index (# of the row) where the value of A2 is in column Ordered Items F:F
See here for a tutorial on it: https://www.youtube.com/watch?v=9sLWDjAEuyc

Related

Plucking specific substring from string - Javascript

I have a dynamic string that is generated like one of the following:
var q = "FROM Table SELECT avg(1), avg(2), avg(3) where x='y'
var q = "SELECT avg(1), avg(2), avg(3) FROM Table where z='x' since x days ago
The values after the select are also dynamic where there could be 1 select option, or 10. I'm trying to create some logic to always pluck whatever is selected into an array, but having trouble dealing with the dynamic nature (string being constructed dynamically AND the # of selects being dynamic).
Basically, end result something like this:
['avg(1)', 'avg(2)', 'avg(3)']
Currently I'm doing something like the following, but it always expects the string to be formatted in a certain order (always starting with SELECT and where after the fields to pluck):
let splitQ = q.match(".*SELECT(.*)where");
let selects = splitQ[1].trim().split(",");
Here is a working solution.
It makes these assumptions about the query (after lowercased).
the values come after the first instance of the word 'select '
if the query starts with 'from', values end before the first instance of ' where'
if the query starts with 'select', values end before the first instance of ' from'
const test1 = "FROM Table SELECT avg(1), avg(2), avg(3) where x='y'";
const test2 = "SELECT avg(1), avg(2), avg(3) FROM Table where z='x' since x days ago";
function extractValues(query) {
// in both scenarios, the values always come directly after 'select '
const valuesComeAfterMe = 'select ';
query = query.toLowerCase();
let valuesEndBeforeMe;
// conditionally handle both query syntaxes
if (query.startsWith('from')) {
valuesEndBeforeMe = ' where';
} else if (query.startsWith('select')) {
valuesEndBeforeMe = ' from';
} else {
throw Error('query not handled');
}
// remove start
query = query.slice(query.indexOf(valuesComeAfterMe) + valuesComeAfterMe.length);
// remove end
query = query.slice(0, query.indexOf(valuesEndBeforeMe));
// split values and trim whitespace
return query.split(',').map(item => item.trim());
}
console.log(extractValues(test1));
console.log(extractValues(test2));

Get most current data from Array JavaScript

I have a SP2010 list which contains a column with dates. (Date created).
I pull these using Spservices which comes in the following format:YYYY.MM.DD 00:00.
In the main function which reads the list I split the date so all that remains is YYYY.MM.DD.
dateAdded = ($(this).attr("ows_DatumActualiteit")).split(" ")[0];
The dates are then pushed in to an array for later filtering.
function selectMostRecentAct(datesIn){
var dateArray = [];
dateArray.push(datesIn);
for (var i = 0; i < dateArray.length; i++) {
console.log(dateArray[i]); //spits out all the dates in the array.
};
I want to select the most recent date in this array. How can I achieve this?
This question seems similar to yours:
What is the elegant way to get the latest date from array of objects in client side?
An alternative would be to loop through the whole array and compare the dates. As far as I know, you can use the <, >, == operators with dates.
dateArray.forEach((e) => {
if (e > latestDate)
latestDate = e;
});
console.log('Latest date is: ' + latestDate);
You need to sort the values then take the first element.
I'm assuming the format of your dates, but new Date is very lenient and should accept most reasonable inputs.
const dates = [
"2018-03-01T10:30:12.000Z",
"2018-03-01T12:11:49.000Z",
"2018-03-12T15:54:49.000Z",
"2018-03-09T19:12:49.000Z",
"2018-03-03T01:41:49.000Z",
];
const selectMostRecent = dates =>
dates.sort((a, b) => new Date(b) - new Date(a))[0];
console.log(selectMostRecent(dates));
Sort wants a comparison function that returns a value, either positive, negative or zero. When you perform arithmetic on a date value it is converted to epoch time (e.g. 1522096404277, milliseconds since 01/01/1970), and then subtracting these gives us the signed value we desire.
For example,
2018-03-09T19:12:49.000Z returns 1520622769000
2018-03-03T01:41:49.000Z returns 1520041309000
And when we do 2018-03-09T19:12:49.000Z - 2018-03-03T01:41:49.000Z (but those are coerced to numbers as described above) we get 581460000, meaning that the first date is sorted above the latter one.
Maybe something like this:
dateArray
.map(d => new Date(d.replace(/\./g, "-"))) // ISO format
.sort((a, b) => b - a) // Newest to oldest, probably
[0] // First!
This uses a reusable function for max; it should work for any values that you can compare with >. Note that it requires you to set a minimum value. For strings, "" (the empty string) seems appropriate. For numbers, you could use -Infinity, although that case is already covered by Math.max. For actual dates (not these dates formatted as strings), you can choose any date that would be well before any conceivable values, perhaps something like new Date(-1e15).
const max = (min) => vals => vals.reduce((curr, val) => val > curr ? val : curr, min)
const maxDate = max('')
console.log(
maxDate(['2014.07.23', '2014.08.29', '2007.03.25', '2017.09.30', '2008.02.29'])
)

Find missing quarters in time series

I am trying to see how could I fill the missing financial quarters of a time series like this in Javascript:
["2012-Q2","2012-Q4","2013-Q4","2014-Q1","2014-Q2","2014-Q3",
"2014-Q4","2015-Q1","2015-Q2","2015-Q3","2015-Q4","2016-Q1",
"2016-Q2","2016-Q3","2016-Q4","2017-Q1","2017-Q2","2017-Q3",
"2017-Q4","2018-Q1"]
I would like somehow to get a time series with the missing elements i.e. for each year I should see 4 "dates".
I don't mind ignoring the first quarter before the first element "2012-Q2" and the last 3 quarters after the last element "2018-Q1".
I know moment.js has functions like quarter() or fquarter() (via a plugin), but I am looking for something closer to the other way around. I already have the quarters (as date-strings), and I have to parse them as date objects.
I need to fill the "quarter holes" in between those input string values.
In my case I probably need to parse first those date-strings in that custom format to make them something moment could understand, but I am a bit lost. In here https://momentjs.com/docs/#/parsing/string-format/ a potential format could involve Y for years and Q for quarters, but I am not sure how to escape the literal Q inside every input date-string of that array?
Also assuming I could somehow parse all those date strings into moment objects, then I am not sure how that could help in filling the holes?
I can not find a pure javascript solution involving date types.
Another approach could be to parse those date-strings and get the year and the quarter number using substring and then manually filling the holes checking year/quarter pairs, is there anything simpler than this?
Assuming that you want a full list of quarters between the first one of your input array until the last one, you can:
parse with moment the first and the last element of your array, using moment(String, String) with 'YYYY[-Q]Q' as format parameter, see Escaping charaters section of the docs.
loop from start to end using isSameOrBefore (or other query functions) adding 1 quarter on each iteration (add(1, 'Q'))
Here a live sample:
var quarters = ["2012-Q2","2012-Q4","2013-Q4","2014-Q1","2014-Q2","2014-Q3",
"2014-Q4","2015-Q1","2015-Q2","2015-Q3","2015-Q4","2016-Q1",
"2016-Q2","2016-Q3","2016-Q4","2017-Q1","2017-Q2","2017-Q3",
"2017-Q4","2018-Q1"];
var format = 'YYYY[-Q]Q';
var start = moment(quarters[0], format);
var end = moment(quarters[quarters.length-1], format);
var results = [];
while( start.isSameOrBefore(end) ){
results.push(start.format(format));
start.add(1, 'Q');
}
console.log(results);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>
Why not just make a function that returns the quarters from a particular range?
function getQuarters(startYear, endYear){
var times = [];
for(var i = startYear; i <= endYear; i++){
times.push(i + "-Q1");
times.push(i + "-Q2");
times.push(i + "-Q3");
times.push(i + "-Q4");
}
return times;
}
Calling:
getQuarters(2017,2017);
Returns:
["2017-Q1", "2017-Q2", "2017-Q3", "2017-Q4"]
Assuming you want to get an array with the missing values, you could take a start quarter and cehck against the given data for either pushing the quartal or incremet the index of the array.
function incQ(time) {
time[1]++;
if (time[1] === 5) {
time[0]++;
time[1] = 1;
}
}
var quarters = ["2012-Q2", "2012-Q4", "2013-Q4", "2014-Q1", "2014-Q2", "2014-Q3", "2014-Q4", "2015-Q1", "2015-Q2", "2015-Q3", "2015-Q4", "2016-Q1", "2016-Q2", "2016-Q3", "2016-Q4", "2017-Q1", "2017-Q2", "2017-Q3", "2017-Q4", "2018-Q1"],
actual = quarters[0].split('-Q'),
missing = [],
i = 0;
while (i < quarters.length) {
if (actual.join('-Q') !== quarters[i]) {
missing.push(actual.join('-Q'));
} else {
i++;
}
incQ(actual);
}
console.log(missing);

A string of form nnnn-nnnn is displayed in a spreadsheet as a date or otherwise incorrectly

I have a script I have been using in my test environment to programmically create a tracking number by parsing the year from timestamp and padding the response index.
function setTrackingNumber(ss, lastRowInx, createDateColumn) //This block generates and stores a tracking number in Column AU on the backend
{
var padTrackNo = "" + lastRowInx;
var trackSize = 4;
var trackingNumberColumn = createDateColumn-3; //trackingNumberColumn is currently in AU (Column 47) Calculating using it's relative position to createDateColumn Position
if (ss.getRange(lastRowInx, trackingNumberColumn).getValue() == "") // so that subsequent edits to Google Form don't overwrite original tracking number
{
if (padTrackNo > trackSize)
{
var padTrackNo = pad(padTrackNo, trackSize);
}
else {} //do nothing
var shortYear = setShortYear(ss, lastRowInx, createDateColumn);
var trackingNumber = shortYear + "-" + padTrackNo;
var createTrackingNumber = ss.getRange(lastRowInx, trackingNumberColumn);
createTrackingNumber.setValue(trackingNumber);
}
else {} //should do nothing
return;
}//This is the end of the setTrackingNumber function
function setShortYear(ss, lastRowInx, createDateColumn)
{
var newCreateDate = ss.getRange(lastRowInx,createDateColumn).getValue();
var strDate = "" + newCreateDate;
var splitDate = strDate.split(" ");
var trimYear = splitDate[3];
var shortYear = trimYear;
return shortYear;
}//This is the end of the shortYear function
function pad(padTrackNo, trackSize)
{
while (padTrackNo.length < trackSize)
{
padTrackNo = "0"+padTrackNo;
}
return padTrackNo;
}//This is the end of pad function
That gets me test result which is as expected ex. 2016-0005. However when we added it to another production sheet it seemed to work with test data and then production data showed up like a date 3/1/2016. production result - first cell.
I thought it must just be formatting the string as a date because of the numbers so I tried formatted the column as plain text but that just changed the date to a plain text version of the date.
I thought this might be similar to needing to specify the format like I did in this question Appending initial timestamp from Google Form to end of record in order to permanently store create date onFormSubmit at #SandyGood 's suggestion so I tried setting the number format as [0000-0000] by changing
createTrackingNumber.setValue(trackingNumber);
to
createTrackingNumber.setValue(trackingNumber).setNumberFormat("0000-0000");
which resulted in the [production result - second cell] which again doesn't match the expected result.
Oddly, some submissions seem to work just fine like [production result - third cell]. Over the past 3 days and approximately 10 records it has been fine, then hinky, then fine, they hinky, then fine again. I am not really sure what else to try to debug this odd behaviour.
Note: I had to parse the date as a string as I was having trouble getting it to parse the date correctly from the create date which is taken from initial timestamp.
To my understanding, "2016-0005" is not a number but a string, so the cell containing it should be formatted as plain text. With a script, this can be done by
range.setNumberFormat('#STRING#')
(source), and this must be done before you set the value to the cell. Like this:
createTrackingNumber.setNumberFormat('#STRING#').setValue(trackingNumber);

Find the oldest date in a list of dates

I need a help in coming up with function in JavaScript. The function should be able to find the oldest date from the list of dates in this format yyyy-mm-dd hh:mm:ss. So the function would receive a text and among the lines it should find the oldest date entry and select text associated with that date.
Also if there is Java solution for this problem that could work to and I will use something to include Java inside JavaScript.
This'd work, assuming datelist is the string and that each date is on its own line:
var oldest = (function() {var o = ":", c, a=datelist.split(/\r?\n/); while(c=a.shift()) o = o < c ? o : c; return o;})();
Breaking that down, here's how it works: It's basically creating a function, running it, then getting its return value. The function is like so:
var o = ":",
// ":" comes after "9" in the character map, so it will be greater than any date
c, a = datelist.split(/\r?\n/);
// split on newlines, accomodating both \r and `\r\n` options.
while(c = a.shift()) {
// basically loop through each date
o = o < c ? o : c;
// if the current oldest date is older than the one we're looking at, keep the old one
// otherwise the new date is older so should be kept
}
return o;
// return the result
Here is how you would do it...
Iterate over the dates as an array, turning them into Unix epochs.
Find the oldest with Math.min.apply(Math, arrayOfDates) or better yet Math.min(...arrayOfDates).
You can convert all the date strings to Date objects, and then sort your list numerically and get the first item. Alternatively you can just apply Math.min on it to get the lowest value, if you don't need a sorted list.
var minDate = Math.min.apply(null, datestrings.map(Date));
Or, as your format does have leading zeroes, a simple string sort will do the same. To only search for a minimum string, you can use this:
var min = datestrings.reduce(function(min, cur) {
return cur < min ? cur : min;
});
A Wibbly-wobbly dollarsy-wollarsy example using jquery:
$("body").append($("<div>").attr("id", "q")); //some gratuitous jquery
var timelist = "2012-03-03 10:14:21 \r\n 2012-05-15 21:21:12\r\n 2012-07-01 10:19:19\r\n2012-02-11 21:21:12";
var datelist = timelist.split("\r\n");
var oldest = ":";
$.each(datelist, function (a) {
var trimmedThis = $.trim(this);
if (trimmedThis < latest) oldest = trimmedThis;
});
$("#q").text(oldest); //just to beef up the $ count

Categories

Resources