I have an array of objects with an hour(fc_start_time) property and I want to sort it by this property. I've tried this but it does not work for me.
This is the object structure:
Object {
id="540-events",
local_id=540,
title="Mas Flow",
fc_start_time="12:30 pm"
},
Object {
id="531-events",
local_id=531,
title="Slowly",
fc_start_time="04:30 pm"
},
Object {
id="531-events",
local_id=531,
title="Slowly",
fc_start_time="08:30 am"
}
Thanks in advance.
Dates and time, eh? I hate them. Let's try to figure this out.
How you sort an array? With help of array's sort method. By default this method sorts strings, but we need custom compare function here.
How can we compare time in strings? We can use Date object, parse time, get milliseconds from 1970, and compare them. But we have am-pm format here. So, to use Date.parse method, we need to convert time to 24 hour format first (or use Date.js library).
function convertTo24Hour(time) {
var hours = parseInt(time.substr(0, 2));
if(time.indexOf('am') != -1 && hours == 12) {
time = time.replace('12', '0');
}
if(time.indexOf('pm') != -1 && hours < 12) {
time = time.replace(hours, (hours + 12));
}
return time.replace(/(am|pm)/, '');
}
After we get 24h time, we can parse it like so (don't look at date before time, it doesn't matter, all we care about is time):
Date.parse( '9/19/2014 ' + convertTo24Hour(time) );
Now we can use this in array.sort compare function. We just compare two numbers, 1411129800000 ? 1411132800000, decide which one is bigger and sort array.
function compare(a,b) {
var atime = Date.parse('9/19/2014 ' + convertTo24Hour(a.time));
var btime = Date.parse('9/19/2014 ' + convertTo24Hour(b.time));
if (atime < btime) {
return -1;
}
if (atime > btime) {
return 1;
}
return 0;
}
What we got after all:
Use array.sort compare function to sort elements
Convert time to number so we can correctly compare them
To do so, convert 12h time format to 24 and use Date.parse
Here is jsfiddle to play with - http://jsfiddle.net/rqgmkdbs/
Try this compare function according to the same time input as your example. It compares on the Floating notation of you time value.
<script>
var objs = [
{id:"540-events",local_id:540,title:"Mas Flow",fc_start_time:"12:30 pm"},
{id:"540-events",local_id:540,title:"Mas Flow",fc_start_time:"10:30 pm"},
{id:"540-events",local_id:540,title:"Mas Flow",fc_start_time:"12:30 pm"},
{id:"540-events",local_id:540,title:"Mas Flow",fc_start_time:"14:30 pm"},
{id:"540-events",local_id:540,title:"Mas Flow",fc_start_time:"09:30 pm"}
]
function compare(a,b) {
var time1 = parseFloat(a.fc_start_time.replace(':','.').replace(/[^\d.-]/g, ''));
var time2 = parseFloat(b.fc_start_time.replace(':','.').replace(/[^\d.-]/g, ''));
if(a.fc_start_time.match(/.*pm/)) time1 += 12; if(b.fc_start_time.match(/.*pm/)) time2 += 12;
if (time1 < time2) return -1;
if (time1 > time2) return 1;
return 0;
}
objs.sort(compare);
console.log(objs);
</script>
var input = [{hour:1, minutes:10},{hour:4, minutes: 1}, ...];
input.sort(function (a, b)
{
// compare hours first
if (a.hour < b.hour) return -1;
if (a.hour > b.hour) return 1;
// else a.hour === b.hour, so compare minutes to break the tie
if (a.minute < b.minute) return -1;
if (a.minute > b.minute) return 1;
// couldn't break the tie
return 0;
});
Try this. You have to consider am and pm in your time.
<!doctype html>
</html>
<head>
<meta charset="utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
// The data
var data = [
{ id:"540-events", local_id:540, title:"Mas Flow", fc_start_time:"12:30 pm"},
{ id:"531-events", local_id:531, title:"Slowly", fc_start_time:"04:30 pm"},
{ id:"545-events", local_id:545, title:"Mas Flow 2", fc_start_time:"03:30 am"}
]
// Sort values
data.sort(function(a,b){
var aValue = new Number(a.fc_start_time.replace(/\d*/g,""));
var bValue = new Number(b.fc_start_time.replace(/\d*/g,""));
if( aTime.match(/.*pm/) ){
aValue + 12;
}
return aValue - bValue;
});
// Show values
for( var i = 0; i < data.length; i++){
$("ul").append("<li>"+data[i].fc_start_time+"</li>");
}
});
</script>
</head>
<body>
<ul>
</ul>
</body>
</html>
Related
I have this function:
function getInfoSchoolTime() {
var date = new Date();
var schoolBellTime = ["8:10","9:02","9:54","9:59","10:51","11:43","11:58","12:48","13:35","13:40","14:10","15:02","15:54"];
var remainingTime, currentHour;
for (var i = 0; i < schoolBellTime.length-1; i++) {
var startTime = schoolBellTime[i].split(":");
var endTime = schoolBellTime[i+1].split(":");
if (parseInt(startTime[0]) >= date.getHours() && parseInt(startTime[1]) >= date.getMinutes())
if (parseInt(endTime[0]) <= date.getHours() && parseInt(endTime[1]) <= date.getMinutes()) {
currentHour = i;
remainingTime=(parseInt(endTime[1])-date.getMinutes()+60)%60;
break;
}
}
if (currentHour == undefined)
return {current: -1, remaining: "not available"};
return {current: currentHour, remaining: remainingTime};
}
var info = getInfoSchoolTime();
console.log(info.current, info.remaining);
I have the schoolBellTime array that contains the timestamps of my school bell (I know, my school has strange bell times, these timestamps includes playtimes and lunchtime), this function is meant to return the 1st hour/2nd hour/3rd hour ... and the minutes that remains to the next hour/breaktime.
I checked all the code and can't find the error, it keeps returning {current: -1, remaining: "not available"}
The function at the top: setDateTime() takes a date and a time, and constructs a date object for that time.
Then I updated your function, I convert start and end to times on the current day, and then check if date.getTime() occurs between them. Then I simply subtract date.getTime() from end, and convert the result to minutes from milliseconds.
var setDateTime = function(date, str) {
var sp = str.split(':');
date.setHours(parseInt(sp[0], 10));
date.setMinutes(parseInt(sp[1], 10));
return date;
}
function getInfoSchoolTime() {
var date = new Date();
var schoolBellTime = ["8:10", "9:02", "9:54", "9:59", "10:51", "11:43", "11:58", "12:48", "13:35", "13:40", "14:10", "14:10", "15:02", "15:54"];
var remainingTime, currentHour, currentPeriod;
for (var i = 0; i < schoolBellTime.length - 1; i++) {
start = setDateTime(new Date(), schoolBellTime[i])
end = setDateTime(new Date(), schoolBellTime[i + 1])
if (date.getTime() > start.getTime() && date.getTime() < end.getTime()) {
currentHour = i
remainingTime = end.getTime() - date.getTime()
currentPeriod = ([schoolBellTime[i], schoolBellTime[i+1]]).join('-')
}
}
return {current: currentHour, currentPeriod: currentPeriod, remaining: Math.round(remainingTime * 0.0000166667)}
}
console.log(getInfoSchoolTime())
Here's a somewhat different approach, both to the code and the API. It uses two helper functions. Each should be obvious with a single example: pad(7) //=> "07" and pairs(['foo', 'bar', 'baz', 'qux']) //=> [['foo', 'bar'], ['bar', 'baz'], ['baz', 'qux']].
The main function takes a list of bell times and returns a function which itself accepts a date object and returns the sort of output you're looking for (period, remaining time in period.) This API makes it much easier to test.
const pad = nbr => ('00' + nbr).slice(-2)
const pairs = vals => vals.reduce((res, val, idx) => idx < 1 ? res : res.concat([[vals[idx - 1], val]]), [])
const schoolPeriods = (schoolBellTime) => {
const subtractTimes = (t1, t2) => 60 * t1.hour + t1.minute - (60 * t2.hour + t2.minute)
const periods = pairs(schoolBellTime.map(time => ({hour: time.split(':')[0], minute: +time.split(':')[1]})))
return date => {
const current = {hour: date.getHours(), minute: date.getMinutes()}
if (subtractTimes(current, periods[0][0]) < 0) {
return {message: 'before school day'}
}
if (subtractTimes(current, periods[periods.length - 1][1]) > 0) {
return {message: 'after school day'}
}
const idx = periods.findIndex(period => subtractTimes(current, period[0]) >= 0 && subtractTimes(period[1], current) > 0)
const period = periods[idx]
return {
current: idx + 1,
currentPeriod: `${period[0].hour}:${pad(period[0].minute)} - ${period[1].hour}:${pad(period[1].minute)}`,
remaining: subtractTimes(period[1], current)
}
}
}
const getPeriod = schoolPeriods(["8:10","9:02","9:54","9:59","10:51","11:43","11:58","12:48","13:35","13:40","14:10","14:10","15:02","15:54"])
console.log("Using current time")
console.log(getPeriod(new Date()))
console.log("Using a fixed time")
console.log(getPeriod(new Date(2017, 11, 22, 14, 27))) // will Christmas break ever come?!
I made a random guess at the behavior you would want if the date is outside the period range.
Internally, it creates a list of period objects that look like
[{hour:9, minute: 59}, {hour: 10, minute: 51}]
Perhaps it would be cleaner if instead of a two-element array it was an object with start and end properties. That would be an easy change.
Do note that for this to make sense, the bells need to be listed in order. We could fix this with a sort call, but I don't see a good reason to do so.
Here is an ES6 example using deconstruct (const [a,b]=[1,2]), array map, array reduce, partial application (closure) and fat arrow function syntax.
This may not work in older browsers.
//pass date and bellTimes to function so you can test it more easily
// you can partially apply bellTimes
const getInfoSchoolTime = bellTimes => {
//convert hour and minute to a number
const convertedBellTimes = bellTimes
.map(bellTime=>bellTime.split(":"))//split hour and minute
.map(([hour,minute])=>[new Number(hour),new Number(minute)])//convert to number
.map(([hour,minute])=>(hour*60)+minute)//create single number (hour*60)+minutes
.reduce(//zip with next
(ret,item,index,all)=>
(index!==all.length-1)//do not do last one, create [1,2][2,3][3,4]...
? ret.concat([[item,all[index+1]]])
: ret,
[]
);
return date =>{
//convert passed in date to a number (hour*60)+minutes
const passedInTime = (date.getHours()*60)+date.getMinutes();
return convertedBellTimes.reduce(
([ret,goOn],[low,high],index,all)=>
//if goOn is true and passedInTime between current and next bell item
(goOn && passedInTime<high && passedInTime>=low)
? [//found the item, return object and set goOn to false
{
current: index+1,
currentPeriod: bellTimes[index]+"-"+bellTimes[index+1],
remaining: high-passedInTime
},
false//set goOn to false, do not continue checking
]
: [ret,goOn],//continue looking or continue skipping (if goOn is false)
[
{current: 0, currentPeriod: "School is out", remaining: 0},//default value
true//initial value for goOn
]
)[0];//reduced to multiple values (value, go on) only need value
}
};
//some tests
const date = new Date();
//partially apply with some bell times
const schoolTime = getInfoSchoolTime(
[
"8:10", "9:02", "9:54", "9:59", "10:51",
"11:43", "11:58", "12:48", "13:35", "13:40",
"14:10", "14:10", "15:02", "15:54"
]
);
//helper to log time from a date
const formatTime = date =>
("0"+date.getHours()).slice(-2)+":"+("0"+date.getMinutes()).slice(-2);
date.setHours(11);
date.setMinutes(1);
console.log(formatTime(date),schoolTime(date));//11:01
date.setHours(15);
date.setMinutes(53);
console.log(formatTime(date),schoolTime(date));//15:53
date.setHours(23);
date.setMinutes(1);
console.log(formatTime(date),schoolTime(date));//23:01
How can I create an array of dates with format DD-MM-YYYY from today and 1 ahead?
I guess it's something like
var dates = [];
var date = moment();
while (date <= date.clone().add(1, 'month')) {
dates.push(date.format('DD-MM-YYYY'));
date = date.clone().add(1, 'd');
}
but is this the best way to do it?
And how can I do the same with minutes? I want an array with ['00:00', '00:05', '00:10', ..., '23:50', '23:55'].
I guess it's something like
var minutes = [];
var time = moment('00:00', 'hh:mm');
while (time < time.clone().add(1, 'day')) {
minutes.push(time.format('hh:mm'));
time = time.clone().add(5, 'minutes');
}
It's not important to use moment.js for this, but I guess it's easier.
Since these can be general functionality, you should make them configurable.
Time Array
For Time array, i guess creating moment object and manipulating its values will be a waste of resource. You can do that with normal loops.
Non moment version
function getDoubleDigits(str) {
return ("00" + str).slice(-2);
}
function formatTime(h, m, is24Hr) {
var tmp = "";
if(is24Hr){
tmp =" " + (Math.floor(h/12) ? "p.m." : "a.m.")
h=h%12;
}
return getDoubleDigits(h) + ":" + getDoubleDigits(m) + tmp;;
}
function getTimeByInterval(interval, is24HrFormat) {
var times = []
for (var i = 0; i < 24; i++) {
for (var j = 0; j < 60; j += interval) {
times.push(formatTime(i, j, is24HrFormat))
}
}
return times.slice(0);
}
console.log(getTimeByInterval(5, false))
console.log(getTimeByInterval(5, true))
Date Array
Since you want dates between 2 dates with a specific interval, its better to make them configurable:
Moment version
I have made even format configurable in this version. This can be done in non-moment version as well but I guess that(how to format date in pure JS) goes out of question's scope and so not doing that.
function getDatesInrange(d1, d2, interval, format){
var dates = [];
while(d1.isBefore(d2)){
dates.push(d1.format(format));
d1.add(interval, "days");
}
console.log(dates)
return dates.slice(0)
}
getDatesInrange(moment(), moment().add(1, "month"), 1, "DD-MM-YYYY")
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>
Non Moment Version
function getDatesInrange(d1, d2, interval){
var dates = [];
while(+d1 < +d2){
dates.push(formateDate(d1));
d1.setDate(d1.getDate() + interval)
}
console.log(dates)
return dates.slice(0)
}
function formateDate(date){
return [getDoubleDigits(date.getDate()),
getDoubleDigits(date.getMonth() +1),
date.getFullYear()].join('-')
}
var startDate = new Date();
var endDate = new Date();
endDate.setMonth(endDate.getMonth() + 1);
getDatesInrange(startDate, endDate, 1)
function getDoubleDigits(str) {
return ("00" + str).slice(-2);
}
Nothing wrong with your current implementation idea (except that the current code will cause an infinite loop. The ceiling should be declared separately), but since moment.js can take objects for duration, it's also possible to create a single helper function in which the values are calculated. For some cases this may not be the most efficient way (for times without a date, this could be overkill), but it would keep it versatile. e.g. if the range should stay the same but only the format should change to include a date, only one parameter would have to be changed.
The example below uses an ES6 generator, but the same could be easily converted to return an array instead:
function* createRange(start, duration, interval, format){
let dt= start.clone(), target = start.clone().add(duration);
while(dt < target){
yield dt.format(format);
dt.add(interval || {years:1});
}
}
let dates = createRange(moment(),{month:1}, {days:1}, 'DD-MM-YYYY'),
times= createRange(moment('00:00','HH:mm'),{days:1},{minutes:5}, 'HH:mm');
console.log([...dates]);
console.log([...times]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>
Of course if certain ranges, such as the month from now, are recurring, they can be put in a separate function which calls the range function.
So have my googledocs document with following 2 functions in javascript:
function test(date) {
return date.getMonth()+1
}
function hours_monthly(target_month, target_year, date_range, hour_range) {
r = 0;
for (i = 0; i < date_range.length ; i++){
if (target_month == date_range[i].getMonth() +1 && date_range[i].getYear() - 2000 == target_year ){
r += hour_range[i]
}
}
return r
}
So now I want to use the code. While the test function is just fine at reading the month of any given date, by assigning "= test(C9)" to cell. Where C9 is a cell which got the date of 6/9/16 assigned to it.
If use a the second funtion it says it can't find the getMonth() function of the exact same date C9 and just outputs ERROR. More precisly it says:
"can't find the getMonth() function in Tue Sept 06 2016 00:00:00 GTM +0200"
date_range is an array of dates, hour_range is an array of integers.
The problem porbably lies somewhere in the usage of arrays.
The same problem arrises when doing comparisions:
function test2(date1, date2) {
if (date1 > date2) {
return 1
}
else {
return 2
}
function hours_period(start_date, end_date, date_range, hour_range){
r = 0;
t = 0;
for (i = 0; i < date_range.length; i++){
if ( (date_range[i] >= start_date) && (date_range[i] <= end_date) ){
t += 1
r += hour_range[i]
}
}
return [t, r]
}
Now again the test2 function works totally fine but as soon as I feed an array (date_range) it stops working, in this case it doesnt give an ERROR instead it just outputs [0,0] no matter how many dates acutally fit the if-statement
You pass date as string and string don't have getMonth method.
Fix it by wrap the date in new Date(date)
function test(date) {
// I wrap the new date you pass with new Date(date)
return new Date(date).getMonth()+1;
}
function hours_monthly(target_month, target_year, date_range, hour_range) {
r = 0;
for (i = 0; i < date_range.length ; i++){
if (target_month == date_range[i].getMonth() +1 && date_range[i].getYear() - 2000 == target_year ){
r += hour_range[i]
}
}
return r
}
console.log(test("6/9/16"));
I have an array of dates formatted as MM/DD/YYYY. I need to find the next closest date in the future starting from today. Say today was 1/22/2016 then 2/19/2016 would return.
2/3/2015
7/5/2015
1/21/2016
2/19/2016
7/1/2016
I've tried doing substrings to get the month, day, year separate and attempting a sort based off those values but surely there has to be a better way.
There is no need for a sorting algorithm. You only need to iterate once and find the closest date that is greater or equals today.
Pseudocode
closest <- infinity
foreach date in dates:
if (date >= now and date < closest) then
closest <- d
return closest
JavaScript
const dates = [
'2/3/2035',
'7/5/2035',
'1/21/2036',
'2/19/2036',
'7/1/2036',
'10/22/2039',
'08/12/2039',
];
const now = new Date();
let closest = Infinity;
dates.forEach(function(d) {
const date = new Date(d);
if (date >= now && (date < new Date(closest) || date < closest)) {
closest = d;
}
});
console.log(closest);
Personally I would use a library such as the very good Moment.JS library, to handle all the horrible complexity of dates.
It has a difference method:
http://momentjs.com/docs/#/displaying/difference/
e.g.
var a = moment([2007, 0, 29]);
var b = moment([2007, 0, 28]);
a.diff(b) // 86400000
It would then be trivial to Math.min() the differences of each date in your list.
There's also a moment.min, which might shortcut this entirely, if all your dates are in the future already:
http://momentjs.com/docs/#/get-set/min/
A naïve implementation would be to parse each date as a string and sort them in ascending order. Then, remove any dates that are in the past, and get the first child of the array of remaining dates. See this jsbin example:
var dates = [
'2/3/2015',
'7/5/2015',
'1/21/2016',
'2/19/2016',
'7/1/2016'
];
// parse each string as a Date object and sort them in ascending order
function sortDates(dates) {
return dates.map(function(date) {
return new Date(date).getTime();
}).sort(function(a, b) {
return a - b;
});
}
var orderedDates = sortDates(dates);
// remove any dates in the past, and get the first child of the array of remaining dates
var nextDate = orderedDates.filter(function(date) {
return (Date.now() - date) > 0;
})[0];
Keep in mind that this depends on the format of the date string that you pass to the Date object (in other words, is 1/12/2015 January 12th, or December 1st? JavaScript will parse it as January 12th.
You can use while loop, new Date()
var dates = ["2/3/2015","7/5/2015","1/21/2016","2/19/2016","7/1/2016"]
, d = "1/22/2016", n = -1, res = null;
while (++n < dates.length && new Date(dates[n]) < new Date(d));
res = dates[n] || d;
console.log(res)
Lots of answers, one more can't hurt.
Date strings should always be manually parsed. A library can help, but if you only have a single format, a simple function is all that's required.
The following uses reduce to loop over the array of dates and finds the closest future date. If no date is in the future, it returns null.
The returned value is the string from the array, not a Date.
function parseMDY(s) {
var b = (s || '').split(/\D/);
return new Date(b[2], b[0]-1, b[1])
}
function getClosestDateToToday(arr) {
var now = new Date();
now.setHours(23,59,59);
return arr.reduce(function (acc, s) {
var d = parseMDY(s);
return d < now? acc : (acc && d > parseMDY(acc)? acc : s);
}, null);
}
var dates = ['2/3/2015', '7/5/2015','1/21/2016',
'2/19/2016','7/1/2016'];
document.write(getClosestDateToToday(dates));
This really depends upon your dates and data structures (the ones shown in original example are not so great for me).
From the other answers...
To take the example from Josh, you could also keep a pointer to which date you are using, or simply shift off of a sorted queue of dates to make it work, but it's really adding noise to your code, disrupting the purpose.
Frederik.L answer is really beautiful code, but it would still have to be executed multiple times, so I cannot recommend it.
Feedback warning
I've been given feedback in comments that Date.parse can behave inconsistently. I'll move to passing a date parsing callback function, and demonstrate Date.UTC usage in the callback for OP-specific date format. Please be careful when defining your own callbacks, and please do not copy-paste.
Suggestion
I'd suggest utilizing Date functions i.e. Date.parse; but also try where possible to get data sources sorted without needing application-level sorting. Then you can store-once and step through the array using array.shift() or similar;
Ideally also YYYY-MM-DD
Four-Digit Year
Two-Digit Month
Two-Digit Day
... (continue from least occurring to most occurring)
sample code
var dates = [
'2/3/2015',
'7/5/2015',
'7/1/2016',
'1/21/2016',
'2/19/2016'
]; // unsorted garbage dates
var DateList = function( dateList, getDate ) {
var sortedDates = dateList.sort( function(a, b) {
return getDate(a) - getDate(b);
});
this.next = function() {
var dt = sortedDates.shift();
sortedDates.push(dt); // comment to remove cyclical nature
return dt;
}
};
// specific implementation parser for this format
var getDisgustingDateFormat = function(dStr) {
var dParts = dStr.split('/');
return new Date(Date.UTC(dParts[2],dParts[0],dParts[1]));
};
var dl = new DateList( dates, getDisgustingDateFormat );
Usage
dl.next(); // "2/3/2015"
dl.next(); // "7/5/2015"
dl.next(); // "1/21/2016"
dl.next(); // "2/19/2016"
dl.next(); // "7/1/2016"
dl.next(); // "2/3/2015"
Hope this helps (Updated for clarity)
What about this version using for of and momentjs:
const getClosestFutureDate = (dates) => {
if (dates.length === 0) {
return null;
}
let minDiff = 0;
for (const date of dates) {
minDiff += minDiff + 30;
var currentDate = moment(date);
if (currentDate.isAfter(moment()) && currentDate.diff(moment(), "days") <= minDiff) {
break;
}
}
return currentDate;
};
Assuming now = 2019-08-21
console.log(getClosestFutureDate(["2019-05-07", "2019-06-01", "2019-07-13", "2019-11-09", "2019-11-10", "2019-11-11"]));
// 2019-11-09
I am fan of momentjs, but this can be easily refactored to use only vanilla Date.
const FindDate = (date, allDate) => {
// moment().diff only works on moment(). Make sure both date and elements in allDate list is in moment
let nearestDate = -1;
allDate.some(d => {
const currentDate = moment(d)
const difference = currentDate.diff(date); // Or date.diff(currentDate) depending on what you're trying to find
if(difference >= 0){
nearestDate = d
}
});
console.log(nearestDate)
}
In Livescript:
x =
* "2/3/2015"
* "7/5/2015"
* "1/21/2016"
* "2/19/2016"
* "7/1/2016"
sim-unix-ts = (date-str) ->
# Simulate unix timestamp like concatenating
# convert "MM/DD/YYYY" to YYYYMMDD (integer)
# so we can simply compare these integers
[MM, DD, YYYY] = date-str.split "/"
MM = "0#{MM}".slice -2 # apply zero padding
DD = "0#{DD}".slice -2 # apply zero padding
parse-int "#{YYYY}#{MM}#{DD}"
today = sim-unix-ts "2/18/2016"
date-list = [sim-unix-ts(..) for x]
# find next date
next-dates = [.. for date-list when .. > today]
next-date = next-dates.0
next-date-orig = x[date-list.index-of next-date]
alert [next-date, next-date-orig]
..in Javascript:
var x, simUnixTs, today, dateList, res$, i$, x$, len$, nextDates, y$, nextDate, nextDateOrig;
x = ["2/3/2015", "7/5/2015", "1/21/2016", "2/19/2016", "7/1/2016"];
simUnixTs = function(dateStr){
var ref$, MM, DD, YYYY;
ref$ = dateStr.toString().split("/"), MM = ref$[0], DD = ref$[1], YYYY = ref$[2];
MM = ("0" + MM).slice(-2);
DD = ("0" + DD).slice(-2);
return parseInt(YYYY + "" + MM + DD);
};
today = simUnixTs("2/18/2016");
res$ = [];
for (i$ = 0, len$ = x.length; i$ < len$; ++i$) {
x$ = x[i$];
res$.push(simUnixTs(x$));
}
dateList = res$;
res$ = [];
for (i$ = 0, len$ = dateList.length; i$ < len$; ++i$) {
y$ = dateList[i$];
if (y$ > today) {
res$.push(y$);
}
}
nextDates = res$;
nextDate = nextDates[0];
nextDateOrig = x[dateList.indexOf(nextDate)];
alert([nextDate, nextDateOrig]);
How can I find out the min and the max date from an array of dates? Currently, I am creating an array like this:
var dates = [];
dates.push(new Date("2011/06/25"))
dates.push(new Date("2011/06/26"))
dates.push(new Date("2011/06/27"))
dates.push(new Date("2011/06/28"))
Is there a built-in function to do this or am I to write my own?
Code is tested with IE,FF,Chrome and works properly:
var dates=[];
dates.push(new Date("2011/06/25"))
dates.push(new Date("2011/06/26"))
dates.push(new Date("2011/06/27"))
dates.push(new Date("2011/06/28"))
var maxDate=new Date(Math.max.apply(null,dates));
var minDate=new Date(Math.min.apply(null,dates));
Something like:
var min = dates.reduce(function (a, b) { return a < b ? a : b; });
var max = dates.reduce(function (a, b) { return a > b ? a : b; });
Tested on Chrome 15.0.854.0 dev
Same as apply, now with spread :
const maxDate = new Date(Math.max(...dates));
(could be a comment on best answer)
_.min and _.max work on arrays of dates; use those if you're using Lodash or Underscore, and consider using Lodash (which provides many utility functions like these) if you're not already.
For example,
_.min([
new Date('2015-05-08T00:07:19Z'),
new Date('2015-04-08T00:07:19Z'),
new Date('2015-06-08T00:07:19Z')
])
will return the second date in the array (because it is the earliest).
Since dates are converted to UNIX epoch (numbers), you can use Math.max/min to find those:
var maxDate = Math.max.apply(null, dates)
// convert back to date object
maxDate = new Date(maxDate)
(tested in chrome only, but should work in most browsers)
**Use Spread Operators| ES6 **
let datesVar = [ 2017-10-26T03:37:10.876Z,
2017-10-27T03:37:10.876Z,
2017-10-23T03:37:10.876Z,
2015-10-23T03:37:10.876Z ]
Math.min(...datesVar);
That will give the minimum date from the array.
Its shorthand Math.min.apply(null, ArrayOfdates);
ONELINER:
var min= dates.sort((a,b)=>a-b)[0], max= dates.slice(-1)[0];
result in variables min and max, complexity O(nlogn), editable example here. If your array has no-date values (like null) first clean it by dates=dates.filter(d=> d instanceof Date);.
var dates = [];
dates.push(new Date("2011-06-25")); // I change "/" to "-" in "2011/06/25"
dates.push(new Date("2011-06-26")); // because conosle log write dates
dates.push(new Date("2011-06-27")); // using "-".
dates.push(new Date("2011-06-28"));
var min= dates.sort((a,b)=>a-b)[0], max= dates.slice(-1)[0];
console.log({min,max});
var max_date = dates.sort(function(d1, d2){
return d2-d1;
})[0];
if you get max or min date with string type from string date of array,you can try this:
const dates = ["2021-02-05", "2021-05-20", "2021-01-02"]
const min = dates.reduce((acc,date)=>{return acc&&new Date(acc)<new Date(date)?acc:date},'')
const max = dates.reduce((acc,date)=>{return acc&&new Date(acc)>new Date(date)?acc:date},'')
The above answers do not handle blank/undefined values to fix this I used the below code and replaced blanks with NA :
function getMax(dateArray, filler) {
filler= filler?filler:"";
if (!dateArray.length) {
return filler;
}
var max = "";
dateArray.forEach(function(date) {
if (date) {
var d = new Date(date);
if (max && d.valueOf()>max.valueOf()) {
max = d;
} else if (!max) {
max = d;
}
}
});
return max;
};
console.log(getMax([],"NA"));
console.log(getMax(datesArray,"NA"));
console.log(getMax(datesArray));
function getMin(dateArray, filler) {
filler = filler ? filler : "";
if (!dateArray.length) {
return filler;
}
var min = "";
dateArray.forEach(function(date) {
if (date) {
var d = new Date(date);
if (min && d.valueOf() < min.valueOf()) {
min = d;
} else if (!min) {
min = d;
}
}
});
return min;
}
console.log(getMin([], "NA"));
console.log(getMin(datesArray, "NA"));
console.log(getMin(datesArray));
I have added a plain javascript demo here
and used it as a filter with AngularJS in this codepen
This is a particularly great way to do this (you can get max of an array of objects using one of the object properties): Math.max.apply(Math,array.map(function(o){return o.y;}))
This is the accepted answer for this page:
Finding the max value of an attribute in an array of objects
Using only one loop in typescript you can get min/max date values at the same time:
function minMaxDates(dates: Date[]): {minDate: Date, maxDate: Date} {
let minDate = new Date(275760, 8, 13);
let maxDate = new Date(1970, 1, 1);
dates.map(date => {
minDate = minDate < date ? minDate : date;
maxDate = maxDate > date ? maxDate : date;
});
return {minDate, maxDate}
};
To call this function:
const {minDate, maxDate} = minMaxDates(dates);
I posted a demo here.
Using Moment, Underscore and jQuery, to iterate an array of dates.
Sample JSON:
"workerList": [{
"shift_start_dttm": "13/06/2017 20:21",
"shift_end_dttm": "13/06/2017 23:59"
}, {
"shift_start_dttm": "03/04/2018 00:00",
"shift_end_dttm": "03/05/2018 00:00"
}]
Javascript:
function getMinStartDttm(workerList) {
if(!_.isEmpty(workerList)) {
var startDtArr = [];
$.each(d.workerList, function(index,value) {
startDtArr.push(moment(value.shift_start_dttm.trim(), 'DD/MM/YYYY HH:mm'));
});
var startDt = _.min(startDtArr);
return start.format('DD/MM/YYYY HH:mm');
} else {
return '';
}
}
Hope it helps.