Check whether quarters are in sequential order using jQuery - javascript

I am having quarter list.
var quarters = new Array();
quarters.push("Q1 2014");
quarters.push("Q2 2014");
quarters.push("Q4 2014");
quarters.push("Q1 2015");
Here quarters are not in sequence. "Q3 2014" is missing.
How to check whether these are in sequence or not using jquery?

Sort the array (you'll need a callback comparison function to pass to sort)
Loop through the array comparing the "current" entry against the previous, looking to see if it's exactly one quarter later
In both cases, you'll probably need something that parses the strings so you have a quarter number and a year, rather than a string combining them. The parsing is trivial enough that you can use regex for it:
var parts = str.match(/^Q(\d) (\d{4})$/);
...and then parts[1] is the quarter and parts[2] is the year.
I'm not usually a fan of providing all the code, but as there's now incorrect code kicking around the question:
var quarters = [
"Q1 2014",
"Q2 2014",
"Q4 2014",
"Q1 2015"
];
var prev, i, curr;
// Sort the quarters according to their quarter value (see `qval` below)
quarters.sort(function(a, b) {
return qval(a) - qval(b);
});
// Find gaps by starting at the beginning and comparing each entry
// to the one just in front of it
prev = qval(quarters[0]);
for (i = 1; i < quarters.length; ++i) {
curr = qval(quarters[i]);
if (curr !== prev + 1) {
console.log("Gap found between " + quarters[i - 1] + " and " + quarters[i]);
}
prev = curr;
}
// `qval` parses a quarter string ("Q2 2014", for instance) and returns a
// a number for the quarter: The year times four plus the quarter
// within the year (e.g., 8058 for Q2 2014). For easy comparison.
function qval(quarterString) {
var qv, parts;
parts = quarterString.match(/^Q(\d) (\d{4})$/);
if (!parts) {
throw "Invalid quarter string: '" + quarterString + "'";
}
qv = (parseInt(parts[2]) * 4) + parseInt(parts[1]);
return qv;
}
Live Copy

Related

How to split date strings in JavaScript array

For the following Month/Day/Year datestring array...
const array1 = ["05/31/2022", "06/01/2022", "06/02/2022"]
...I am attempting to configure the array to slice and remove all datestring array items (starting with 01 as Day) if they follow after datestring array items with 31 as Day. Same goes for instances of Day 30 followed by Day 01.
To handle this, I set up a for statement to loop through all of the strings in the array. I then used a split method to remove "/" from each array item, thus breaking MM,DD,YYYY into separate variables.
for (let i = 0; i < array1.length; i++) {
var [month, day, year] = array1[i].split('/');
console.log(month, day, year)
}
My intention for the next step is to set up a conditional that checks if an array item that includes 30 or 31 as "day" is followed by an array item that includes 01 as "day", then use slice to remove subsequent dates faster 30th or 31st. For this part, I attempted to re-consolidate month, day and year into individual array items, like so:
const newArray = []
for (let i = 0; i < array1.length; i++) {
var [month, day, year] = array1[i].split('/');
newArray.push(month + day + year)
console.log(newArray)
}
with output:
 ['05312022', '06012022', '06022022']
However, I'm not sure how to set up a conditional that checks if an array item that includes 30 or 31 as "day" is followed by an array item that includes 01 as "day". How can I go about the functionality for such a check?
You can loop over the dates array and grab the currDay and prevDay and if the condition is satisfied, slice the dates array and return it.
const solution = (dates) => {
for (let i = 1; i < dates.length; i++) {
const currDay = dates[i].slice(3, 5);
const prevDay = dates[i - 1].slice(3, 5);
if ((currDay === "01") & (prevDay === "31" || prevDay === "30")) {
return dates.slice(0, i);
}
}
return dates;
};
console.log(solution(["05/31/2022", "06/01/2022", "06/02/2022"]));
The following us es Array#reduce to
retain all elements unless
the number of elements retained equals the index being considered and the current element has date 01 and follows a 30 or a 31
if index in consideration is greater than the number of items retained, meaning at least one element has been skipped, then the current element is skipped as well.
const array1 = ["05/31/2022", "06/01/2022", "06/02/2022"],
output = array1.reduce(
(prev,cur,i) =>
prev.length && ["30","31"].includes( prev.slice(-1)[0].slice(3,5) ) &&
(prev.length === i && cur.slice(3,5) === "01" || i > prev.length) ?
prev :
[...prev, cur],
[]
);
console.log( output );

Google Scripts - Can't get a result out of the while loop

I have a problem to get an updated result out of a while loop.
Already spent a few days reading articles and trying to figure it out...
Hopefully you can help :)
I want to take a title (string) and if its length is less than 78, I want to add words to it randomlly,
Using a list of words in a sheet.
This is working prefectly, the problem is that its adding same words over and over again.
I searched for a way to remove duplicates and found the [...new Set(arr)], and It works perfectly.
Yet when I try to use it in a while loop (every loop check remove duplicates) , the end result still has duplicates.
I am adding the sheet which will help you understand it better,
Sheet
And here's the code.
/**
* Adds Brand, adds words to 80 letters.
*
* #Param Title the base title used
* #Param Brand the store brand (Ess,Eag..)
* #customfunction
*/
function AddKeyWords(title, brand) {
title = ChangeTitleWords(title, brand); //previous function.
//title = "Just a few words";
if(title == "Missing Brand" || title == "") {
return "Missing Brand or Title";
} else {
let wordsArr = title.split(" "); //split title to array
let len = title.length;
//If title is more than 80 notes, remove words of it.
while (wordsArr.join(" ").length > 80) {
wordsArr.pop();
}
let Title2 = wordsArr.join(" ");
let Title2Len = Title2.length;
let len2 = 80 - Title2Len;
//if title had less than 78 notes, add a random word
while (len2 > 3) {
let wordsArr1 = title.split(" ");
if (len2 == 3) {
wordsArr.push(random3());
} else if (len2 == 4) {
wordsArr.push(random4());
} else if (len2 >= 5 && len2 < 18) {
wordsArr.push(random5());
} else if (len2 >= 18) {
wordsArr.push(random10());
}
wordsArr = [...new Set(wordsArr)]; // create a new arr without duplicates
len2 = 80 - wordsArr.join(" ").length;
//return title3;
title3 = wordsArr;
}
return title3.join(" ");
}
}
////Random Functions.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function random3() {
var t = SpreadsheetApp.getActive().getSheetByName('Settings').getRange('L5:L').getValues().filter(String)
return(t[getRandomInt(0, t.length-1)]);
}
function random4() {
var t = SpreadsheetApp.getActive().getSheetByName('Settings').getRange('M5:M').getValues().filter(String)
return(t[getRandomInt(0, t.length-1)]);
}
function random5() {
var t = SpreadsheetApp.getActive().getSheetByName('Settings').getRange('N5:N').getValues().filter(String)
return(t[getRandomInt(0, t.length-1)]);
}
function random10() {
var t = SpreadsheetApp.getActive().getSheetByName('Settings').getRange('O5:O').getValues().filter(String)
return(t[getRandomInt(0, t.length-1)]);
}
The problem with your code is that you are pushing an array into an array and [...new Set()] does not work on nested arrays
You can easy see the problem implementing logs:
Logger.log("before: ");
Logger.log( wordsArr);
wordsArr = [...new Set(wordsArr)];
Logger.log("after: ");
Logger.log( wordsArr);
The result will be:
Jul 22, 2020, 1:51:06 PM Info before:
Jul 22, 2020, 1:51:06 PM Info [Just, a, few, words, [ggggggggggg], [ggggggggggg]]
Jul 22, 2020, 1:51:06 PM Info after:
Jul 22, 2020, 1:51:06 PM Info [Just, a, few, words, [ggggggggggg], [ggggggggggg]]
Mind that you will not see the same result if you implement
Logger.log("after: " + wordsArr); because concatenation will convert
the array automatically into a string.
Solution
There are many ways to convert your array into a 1-D string.
Without modifying the rest of your code, the easiest would probably be to use flat()
For this simply modify
wordsArr = [...new Set(wordsAr))];
to
wordsArr = [...new Set(wordsArr.flat())];

Javascript Get Sequential Dates in Array cross week and month

I have an array with the following values (example):
[
1491408000000,
1491494400000,
1491753600000,
1493222400000,
1493308800000,
1493568000000
]
Where the index is a date time. The date time will always be at 12:00:00 on a date.
In this example, the first 3 dates are consecutive cross weekend (weekend is holiday so count as leave), then another group of 3 dates cross weekend and month.
Now, what I am trying to do is find sequential dates (cross week and month) and put them into an array as follows:
[
1491408000000,
1491494400000,
1491753600000
],
[
1493222400000,
1493308800000,
1493568000000
]
I have tried the following code to get the sequential dates but this cannot cross week and month, how to modify the code to get above result? Any help would be much appreciated!
var timeValue = new Date(dateReview).getTime();
valueCon.push(timeValue);
var k = 0;
sortedValue[k] = [];
valueCon.sort( function ( a, b ){
return +a > +b ? 1 : +a == +b ? 0: -1;
})
.forEach( function( v , i ){
var a = v,b = valueCon[i+1]||0;
sortedValue[k].push( +a );
if ( (+b - +a) > 86400000) {
sortedValue[++k] = []
}
return 1;
});
sortedValue.sort( function ( a,b ){
return a.length > b.length ? -1: 1;
});
This requires help from a function to test if two dates are in the same week. The following goes over the set of time values provided in an array and puts the first value into an array within the array. For each subsequent value, it tests if it's in the same week as the first value in each array within the outer array.
If it's in the same week as the first value in any existing array, it's pushed into that array. Otherwise, it's put in a new array and pushed into the outer array.
There may be a neater way to implement the algorithm, but I'll leave that for others.
Due to time zone differences, they are adjusted to the host time zone based on the original time values representing noon in the source time zone.
// Given 2 dates, return true if they are in the same week (Mon to Sun).
// Otherwise, return false
function sameWeek(a, b){
var e = new Date(+a);
// Week starts at 00:00:00.000 on Monday on or before date
var s = new Date(e.setDate(e.getDate() - ((e.getDay()||7) -1)));
s.setHours(0,0,0,0);
// Week ends at 23:59:59.999 the following Sunday
e.setDate(e.getDate() + 6);
e.setHours(23,59,59,999);
// Test b and return value
return b >= s && b <= e;
}
// Given time value for UTC-0400, adjust to same date and time
// in local time zone and return a date
function adjust(n) {
var d = new Date(n);
d.setMinutes(d.getMinutes() - 240 + d.getTimezoneOffset());
return d;
}
var result = [1491408000000,1491494400000,1491753600000,1493222400000,1493308800000,1493568000000
].reduce(function(acc, n) {
var d = adjust(n);
var used;
if (acc.length != 0) {
used = acc.some(function(arr) {
if (sameWeek(adjust(arr[0]), d)) {
arr.push(n);
return true;
}
});
}
if (!used || acc.length == 0) {
acc.push([n]);
}
return acc;
},[]);
// Result array
console.log(result);
// Printed as date strings adjusted to same host local time
result.forEach(arr => {
arr.forEach(n => console.log(adjust(n).toString()))
console.log('\n');
});
Manipulation of timestamps is a pain. JavaScript has a built-in Date type, as you know, and I would suggest you use it. Date#getUTCDay returns the day of the week as an integer (for reference, 4 is Friday, or the day before a weekend), while Date#setUTCDate and Date#getUTCDate together allow you to adjust the date in day increments (and have it overflow/underflow to the next/previous month). Thus, to determine whether a timestamp b follows "sequentially" (excluding weekends) after a, you can use:
function sequential (a, b) {
a = new Date(a)
return a.setUTCDate(a.getUTCDate() + (a.getUTCDay() === 4 ? 3 : 1)) === b
}
Grouping is just an exercise after that; the code above contains all of the real logic behind this solution.
Example Snippet
var dates = [
1491408000000,
1491494400000,
1491753600000,
1493222400000,
1493308800000,
1493568000000
]
function sequential (a, b) {
a = new Date(a)
return a.setUTCDate(a.getUTCDate() + (a.getUTCDay() === 4 ? 3 : 1)) === b
}
function groupSequential(dates) {
if (dates.length < 2) return [dates.slice()]
dates.sort(function(a, b) { return a - b })
var result = [], group
for (var i = 0; i < dates.length; i++) {
sequential(dates[i - 1], dates[i]) || result.push(group = [])
group.push(dates[i])
}
return result
}
console.log(groupSequential(dates))

regex a string, convert to number and match condition

I have this string:
002 2.0 (100aa) 95-97
I then want regex the 95-97 portion of it and paste it with relevant two numbers so I get a year.
In example, 96-97 should become 1995-1997, but 00-05 should become 2000-2005 (all numbers between 0 and 16 should be pasted with 20, but all other numbers with 19).
Then, when I have i.e. 1995-1997 I want to check if a year (i.e. 1996) is present inside 1995-1997 interval or not, and return a bolean.
How would one wright such code?
Best Regards
You could use the callback variant of replace:
function parseString(str) {
function padYear(year) {
return year < 30 ? 2000+year :
year < 100 ? 1900+year : year;
}
var result = {};
result.hasCurrentYear = false;
result.str = str.replace(/(\d\d)-(\d\d)$/g, function (match, yr1, yr2) {
yr1 = padYear(+yr1);
yr2 = padYear(+yr2);
var yrNow = (new Date).getFullYear();
result.hasCurrentYear = yrNow >= yr1 && yrNow <= yr2;
return yr1 + '-' + yr2;
});
return result;
}
var str = '002 2.0 (100aa) 95-16';
console.log(parseString(str));
Note that I made the split at year 30, as the solution will become outdated soon if you use 16 as split year.
I suppose there's a much simpler way to check if a certain year is in "range".The solution using String.split, Array.map functions and Number constructor:
var str = "002 2.0 (100aa) 95-97";
function checkYearInRange(str, year) {
year = Number(year);
var range = str.split(" ").pop().split('-').map((v) => Number((Number(v) > 16)? "19"+v : "20"+v));
return (range[0] <= year && year <= range[1]);
}
console.log(checkYearInRange(str, "1996")); // true
console.log(checkYearInRange(str, "2015")); // false

How generate a range of numbers using underscore.js with leading 0s?

I've been trying to generate values for a dropdown where the user is to select hours, minutes.
To get the possible values of minutes, I was using this:
_.range(1, 61)
This generates an array like
[1, 2, 3 ... 60]
but I require it to be of form
[01, 02, 03 ... 60]
Is there a clean way to getting this using underscore?.
In FF, and soon in other browsers, use padStart:
_.range(1, 61) . map(num => String(num).padStart(2, '0'))
Another approach would be
_.range(1, 61) . map(num => ('0' + num).slice(-2));
This tacks a zero on the beginning of all the numbers, but then takes just the last two characters of the result.
A functional approach would be to write a higher-order function which takes the length and returns a function which pads its input to that length:
function make_padder(len) {
return str => ('0000000000000' + str).slice(-len);
}
Now you can write
_.range(1,61) . map(make_padder(2))
Place this at the top of your javascript file ...
Number.prototype.pad = function (count) {
var num = this.valueOf();
var ret = "";
for (var i = 0; i < count - num.toString().length; i++) {
ret += "0";
}
ret += num;
return ret;
}
Then return ...
_.range(1, 61).map(function (num) { return num.pad(2) })
This will give an array of strings with leading 0s of length 2
It is possible only with array of strings:
var
range = _.range(1, 61),
i,
arr = [];
for (i in range) {
if (range[i] <= 9) {
arr.push('0' + range[i]);
}
else {
arr.push('' + range[i]);
}
}
Result:
console.log(arr); // ["01", "02" ... "61"]
Object-Oriented JavaScript - Second Edition When a number starts with a 0, it's considered an octal number. For
example, the octal 0377 is the decimal 255:
var n3 = 0377;
typeof n3; // "number"
n3; // 255
The last line in the preceding example prints the decimal
representation of the octal value.
parseInt('0377', 8); // 255
ECMAScript 5 removes the octal literal values and avoids the confusion
with parseInt() and unspecified radix.

Categories