Find missing quarters in time series - javascript

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);

Related

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'])
)

Adding space to fill array length

I am working with a javascript program that needs to be formatted a certain way. Basically, I need to have each section of information from an array be a set length, for example 12 characters long, and no more than that.
The problem I am running into comes when a value in the array is NOT 12 characters long. If I have a value that is less than the 12 characters the remaining character allotment needs to be filled with blank spaces.
The length of each section of information varies in size and is not always 12. How can I add X number of blank spaces, should the length not meet the maximum requirement, for each section?
This is where I am at with adding space:
str = str + new Array(str.length).join(' ');
I am pretty sure what I have above is wrong but I believe I am on the right track with the .join function. Any ideas?
EDIT: I was asked to show a wanted outcome. It is a bit complicated because this javascript is being run out of a web report tool and not out of something like Visual Studio so its not traditional JS.
The outcome expected should look something like:
Sample Image
So as shown above the data is in one line, cutting off longer strings of information or filling in blank spaces if its too short for the "column" to keep that nice even look.
try this code and leverage the wonders of the map function:
let say your array is:
var myArr = ["123456789012", "12345678901", "123"];
now just apply this function
myArr.map(function(item){ //evalueate each item inside the array
var strLength = item.length; //apply this function to each item
if (strLength < 12){
return item + ' '.repeat(12-item.length) //add the extra spaces as needed
} else {
return item; // return the item because it's length is 12 or +
}
})
What you are looking for is the ' '.repeat(x) - where x is the times you want to repeat the string you have set, it could be '*'.repeat(2) and you would get '**', if you want to understand more about it look at the docs
depending on which version of javascript, this might work:
if (str.length < 12) str += ' '.repeat(12 - str.length);
Not exactly sure how you're setup -- but something like the following will accept an array and return another array with all its values being 12 characters in length.
var array = ['Test', 'Testing', 'Tested', 'This is not a Test'];
var adjustedArray = correctLength(array, 12);
function correctLength(array, length) {
array.map(function(v, i) {
if (array[i].length < length) {
array[i] += Array((length+1) - array[i].length).join('_');
}
// might not need this if values are already no greater than 12
array[i] = array[i].substring(0, length);
});
return array;
}
console.log(adjustedArray);

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);

Read a file with latest Date from a folder using JQuery

I already have a folder, with the files in it with names below in a specific format i.e. MODEL_RELEASEDATE
File names in the folder named Smartphone
SmartphoneA_11122012
SmartphoneA_01022013
SmartphoneA_09102013
SmartphoneA_10072012
SmartphoneA_12042012
**SmartphoneB_08282013**
SmartphoneB_04152013
SmartphoneB_08282012
SmartphoneB_01062013
.
.
.
.
and so on
I want to write a jquery code where I can use a specific keyword from format, from above list, I will pass the value SmartphoneA and I should be able to read the file with the latest release date. Same as in case when I pass the keyword SmartphoneB.
If I pass k/w SmartphoneB, result should be served from file highlighted above, i.e. SmartphoneB_08282013
my current code reads the file name only with specific k/w. I have few alterations to be made.
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
var metaKeywords=$('meta[name=keywords]').attr("content");//this utility works based upon the keywords on the pages.
var reqdKeyword = new Array();
reqdKeyword = metaKeywords.split(",");
var randKeyword = reqdKeyword[Math.floor(Math.random() * reqdKeyword.length)];
var cdnUrl = "http://abc.com/xyz/mobiles/";
var jsnUrl = ".json?callback=showDetail";
var finalUrl= cdnUrl.concat(randKeyword.trim()).concat(jsnUrl);
/*
Rest of the code goes here
*/
</script>
The cool thing about dates is that you can easily sort them if you have the date in "descending" order (i.e., year month day hour second). Using that, we can go through your files to grab just the ones that start with the right prefix, then easily grab the latest one:
var filenames = [
'SmartphoneA_11122012',
'SmartphoneA_01022013',
'SmartphoneA_09102013',
'SmartphoneA_10072012',
'SmartphoneA_12042012',
'SmartphoneB_08282013',
'SmartphoneB_04152013',
'SmartphoneB_08282012',
'SmartphoneB_01062013'
],
whichPhone = 'SmartphoneB', // Dummy value, this would come from user interaction or whatever
possibleFiles = [];
// This goes through your list of filenames and picks out just the ones that match `whichPhone`, then puts them into another array containing a "cleaned-up" date and some other metadata-esque stuff
for (var i = 0, j = filenames.length; i < j; i++) {
var filename = filenames[i];
if (filename.indexOf(whichPhone) > -1) {
possibleFiles.push({
index: i,
filename: filename,
date: parseInt(filename.split('_')[1].replace(/(\d{2})(\d{2})(\d{4})/, function(match, month, day, year) {
return year + month + day;
}), 10)
});
}
}
// Now we go through the `possibleFiles` and figure out which one has the latest date
var latestFileDate = 0,
theActualFilenameYouWantFinally;
for (var i = 0, j = possibleFiles.length; i < j; i++) {
var possibleFile = possibleFiles[i];
if (possibleFile.date > latestFileDate) {
latestFileDate = possibleFile.date;
theActualFilenameYouWantFinally = filenames[possibleFile.index];
}
}
// And, finally, your result
console.log(theActualFilenameYouWantFinally);
EDIT: I didn't use jQuery for this answer because meh, you don't really need jQuery for things like this. Don't get me wrong, John Resig is brilliant, and I use jQuery in almost everything, but for loops are damned fast and easy to work with, and stuff like this isn't really jQuery's strong suit anyhow.
You will need a server-side code to return you a list of URIs (file names), then you can write JavaScript code to parse it (but in this case it is probably better if your server-side code will return the right name right away based on query string). In the worst case scenario you can place a dir.txt file on the server which will be listing all the files in that folder and e.g. run cron job to update it as needed.
jQuery will have no way to list remote files on the server unless your server supports it in one way or another.
Update
Once you have the file you need:
a) tokenize it into an array, e.g. like this
var names = dir.split("\n");
b) leave only strings starting with keyword and cut keyword off
names = $(names).map(function(n,i) {
return (n.indexOf(keyword) == 0) ? n.split('_')[1] : null;
}).get();
now you have an array like this ['11122012', '01022013', ...]
c) find max date in this array
var dateNum = Math.max.apply( null,
$.map(names,function(n,i){ return parseInt(
n.replace(/(\d{2})(\d{2})(\d{4})/, function(match, month, day, year) {
return year + month + day;
}))
}) );
var maxDate = dateNum.toString().replace(/(\d{4})(\d{2})(\d{2})/,
function (match, year, month, day) { return month + day + year; }
);
var fileName = keyword + "_" + maxDate;
voila, your fileName contains the name with max date.
There are other ways of doing it, e.g. really parsing the date into Date object. Also, you can simply iterate your files once, without array mapping and Math.max() iterator. As the amount of code wins over speed here, to find the optimal one depends on where you could re-use its bits and pieces without compromising maintainability.
http://jsfiddle.net/Exceeder/VLB2E/

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