How can i find next smallest Element Key by Value in Object? - javascript

I have an object like this:
times = {
"dawn": "Mon Jul 06 2020 01:34:46 GMT+0200 (Mitteleuropäische Sommerzeit)",
"dusk": "Mon Jul 06 2020 22:43:02 GMT+0200 (Mitteleuropäische Sommerzeit)",
"evening": "Mon Jul 06 2020 19:06:36 GMT+0200 (Mitteleuropäische Sommerzeit)",
"morning": "Mon Jul 06 2020 08:02:55 GMT+0200 (Mitteleuropäische Sommerzeit)"
};
I want to compare the current time with this object and want to get the key as return value, which is closest to the current time.
I try to use this function, but I never get the right key back.
function getKeyByValue(object, value) {
return Object.keys(object).find(key => object[key] === value || object[key] < value);
}
let date = new Date();
getKeyByValue(times, date);
What do I have to change for this to work?

There are two problems here:
You're comparing strings, not dates. Use Date.parse() to get a number, or new Date() to get a Date object.
You're only looking for the first matching value with .find(), not the closest. You'll have to use a more custom solution to find the closest.
This will find the time before the given time which is closest:
// Just so this example works in the future, while still
// comparing against the current time:
const makeTime = (h, m) => {
const today = new Date();
return new Date(today.getFullYear(), today.getMonth(), today.getDate(), h, m);
}
const times = {
"dawn": makeTime(05, 00), // 5:00am
"morning": makeTime(07, 00), // 7:00am
"afternoon": makeTime(12, 00), // 12:00pm
"late afternoon": makeTime(17, 00), // 5:00pm
"dusk": makeTime(19, 00), // 7:00pm
"evening": makeTime(20, 00), // 8:00pm
"night": makeTime(23, 00) // 11:00pm
};
// The important part is here:
const closestTime = Object.entries(times)
.map(([k, v]) => [ k, Date.now() - Date.parse(v) ])
.reduce((pre, cur) => cur[1] < pre[1] && cur[1] > 0 ? cur : pre)[0];
console.log(`Right now, it is ${closestTime}.`);
Here's how it works:
// Get a list of [ key, value ] lists for every key and value in `times`.
const closestTime = Object.entries(times)
// Turn that list of [ key, value ] and turn it into a list of keys and
// the differences between compareTime and the date in the value. Values
// in the future create negative numbers, since their Unix Epoch
// representation is larger than the compared time.
// IE: [ [ 'morning', 12300000 /* (seconds) */ ], ... ]
.map(([k, v]) => [ k, Date.now() - Date.parse(v) ])
// Run through that list, keeping track of the current value only if it
// is smaller than the previous value and positive.
.reduce((pre, cur) => cur[1] < pre[1] && cur[1] > 0 ? cur : pre)
// ↑ returns [ key, value ], and we just want `key`.
[0];
If you want to compare against a specific time, replace Date.now() with Date.parse() and pass in a date string, like Date.parse('Jul 07 2020 11:25:00').

You need to parse the dates in your times object.
Currently, JavaScript is just seeing them as strings so you're comparing a string to a number which returns false.
Try using Date.parse(object[key]). This returns a number.

Convert them to date objects, get the smallest delta, iterate em.
var times = {
"dawn": "Mon Jul 06 2020 01:34:46 GMT+0200 (Mitteleuropäische Sommerzeit)",
"dusk": "Mon Jul 06 2020 22:43:02 GMT+0200 (Mitteleuropäische Sommerzeit)",
"evening": "Mon Jul 06 2020 19:06:36 GMT+0200 (Mitteleuropäische Sommerzeit)",
"morning": "Mon Jul 06 2020 08:02:55 GMT+0200 (Mitteleuropäische Sommerzeit)"
};
var closestKey = null;
var closestDelta = null;
for(key in times)
{
var delta = Math.abs(Date.now() - Date.parse(times[key]));
if(delta < closestDelta || closestDelta == null)
{
closestKey = key;
closestDelta = delta;
}
}
console.log("The closest is:" + closestKey);

Please check it out
times = {
"dawn": "Mon Jul 06 2020 01:34:46 GMT+0200 (Mitteleuropäische Sommerzeit)",
"dusk": "Mon Jul 06 2020 22:43:02 GMT+0200 (Mitteleuropäische Sommerzeit)",
"evening": "Mon Jul 06 2020 19:06:36 GMT+0200 (Mitteleuropäische Sommerzeit)",
"morning": "Mon Jul 06 2020 08:02:55 GMT+0200 (Mitteleuropäische Sommerzeit)"
};
let obj = {}
for (time in times){
Object.assign(obj, {[time]:Math.abs(new Date() - new Date(times[time]).getTime())});
}
//console.log(obj)
function closestDate(obj){
let closestVal = Math.min(...Object.values(obj))
for (item in obj){
if(obj[item] === closestVal){
return item;
}
}
}
console.log(closestDate(obj));

Related

How do I find the nearest Past or Future Date in an array of Dates by an DateObject?

Let's say we have an Array of Dates
var dateArr = [new Date("Thu Apr 01 2021 00:00:00 GMT+0200"), new Date("Sat May 01 2021 00:00:00 GMT+0200")];
and an Date Object, which we need to search in the dateArr, for example:
var findDate = new Date("Mon Apr 05 2021 07:50:06 GMT+0200");
And all together we have this PLUS
a function to return us the nearestDate in
dateArr by findDate which can lie in the past or future
var dateArr = [new Date("Thu Apr 01 2021 00:00:00 GMT+0200"), new Date("Sat May 01 2021 00:00:00 GMT+0200")];
var findDate = new Date("Mon Apr 05 2021 07:50:06 GMT+0200");
var result = getNearestDateInDateArrByFindDate(dateArr, findDate);
console.log(result); //should print to console: Thu Apr 01 2021 00:00:00 GMT+0200
function getNearestDateInDateArrByFindDate(dateArr, findDate) {
var nearestDateInPastOrFuture;
...
return nearestDateInPastOrFuture;
}
What I tried so far without sucess ...
var dateArr = [new Date("Thu Apr 01 2021 00:00:00 GMT+0200"), new Date("Sat May 01 2021 00:00:00 GMT+0200")];
var findDate = new Date("Mon Apr 05 2021 07:50:06 GMT+0200");
function getNearestDateInDateArrByFindDate(dateArr, findDate) {
console.log(dateArr);
console.log(findDate);
var nearestFutureDates = dateArr.filter(dt => dt.getTime() >= findDate.getTime());
var nearestFutureDates = nearestFutureDates.sort((a, b) => a.getTime() - b.getTime());
var nearestPastOrFutureDate = dateArr.filter(dt => dt.getTime() >= findDate.getTime());
var nearestPastOrFutureDate = nearestPastOrFutureDate.sort((a, b) => (findDate.getTime() - a.getTime()) - (findDate.getTime() - b.getTime()));
console.log(nearestFutureDates);
console.log(nearestPastOrFutureDate);
//returns always sat May 01 2021 00:00:00 GMT+0200
}
getNearestDateInDateArrByFindDate(dateArr, findDate)
And somehow the snippet doesn't return Apr 01 but rather April 31?
We can use Array.sort() to sort by the difference in ms from each date to findDate.
NB: We can get the absolute difference in milliseconds between two dates using
Math.abs(date1 - date2);
So we'll use this to sort like so:
var dateArr = [new Date("Thu Apr 01 2021 00:00:00 GMT+0200"), new Date("Sat May 01 2021 00:00:00 GMT+0200")];
var findDate = new Date("Mon Apr 05 2021 07:50:06 GMT+0200");
var result = getNearestDateInDateArrByFindDate(dateArr, findDate);
console.log(result); //should print to console: Thu Apr 01 2021 00:00:00 GMT+0200
function getNearestDateInDateArrByFindDate(dateArr, findDate) {
const sortedByDiff = [...dateArr].sort((a,b) => {
// Sort by the absolute difference in ms between dates.
return Math.abs(a - findDate) - Math.abs(b - findDate);
})
// Return the first date (the one with the smallest difference)
return sortedByDiff[0];
}
You can filter what is the nearest date on past and the nearest on future.
Optionally you can apply .sort((a, b) => a - b) to your array of dates if are not ordered.
const dateArr = [new Date("Thu Apr 01 2021 00:00:00 GMT+0200"), new Date("Sat May 01 2021 00:00:00 GMT+0200")].sort((a, b) => a - b);
const findDate = new Date("Mon Apr 05 2021 07:50:06 GMT+0200");
const nearestPastDate = (dateArr, date) => {
const pastArr = dateArr.filter(n => n <= date);
return pastArr.length > 0 ? pastArr[pastArr.length-1]: null;
};
const nearestFutureDate = (dateArr, date) => {
const futArr = dateArr.filter(n => n >= date);
return futArr.length > 0 ? futArr[0]: null;
};
console.log(dateArr);
console.log(nearestPastDate(dateArr, findDate));
console.log(nearestFutureDate(dateArr, findDate));

How to compare start date and end date in javascript [duplicate]

I have the following string value of a date, Sun Apr 07 2019 00:00:00 GMT-0300, and I need to compare with the following date format 2019-04-08T03:00:00.000Z, note that both are the same day, I need to compare them and return a true as being equal days,
but I do not know how to do this using javascript, any help?
I tried to do something like this but it does not return me true:
if (input) {
//input.value = "Sun Apr 07 2019 00:00:00 GMT-0300 (Horário Padrão de Brasília)"
var situation = angular.isArray(item.situation) ? item.situation : [item.situation];
// situation =
//[
// 0: "2019-04-08T03:00:00.000Z"
// 1: "2019-04-13T03:00:00.000Z"
//]
if (!angular.isArray(input.value)) {
condition = situation.indexOf(input.value) >= 0;
} else if (angular.isArray(input.value)) {
condition = $window._.intersection(situation, input.value).length > 0;
}
}
if (condition) {
break;
}
//input.value = "Sun Apr 07 2019 00:00:00 GMT-0300 (Horário Padrão de Brasília)"
situation =
[
0: "2019-04-08T03:00:00.000Z"
1: "2019-04-13T03:00:00.000Z"
]
Sun Apr 07 2019 00:00:00 GMT-0300
2019-04-08T03:00:00.000Z
note that both are the same day
No, they are not.
You can convert them both to ISO string and just compare their date parts as strings (if I understood the question correctly, you want to compare date only, without time):
function isSameDate(date1, date2) {
const [d1, ] = (new Date(date1)).toISOString().split('T');
const [d2, ] = (new Date(date2)).toISOString().split('T');
return d1 === d2;
}
Convert all the values to Date objects and compare those. Use a framework/library to do it, because parsing strings to dates manually has lots of places where it can go wrong.
Currently you are comparing the literal Strings. Because neither "2019-04-08T03:00:00.000Z", nor "2019-04-13T03:00:00.000Z" match "Sun Apr 07 2019 00:00:00 GMT-0300 (Horário Padrão de Brasília)", your second if statement fails.
Build a date from the strings and compare the days (ie number of seconds since epoch / number of seconds in a day):
const sameDay = (dateString1, dateString2) => {
let time1 = (new Date(dateString1)).getTime();
let time2 = (new Date(dateString2)).getTime();
return Math.floor(Math.abs((time1-time2))/(1000*60*60*24))==0;
}
console.log(
sameDay('Sun Apr 07 2019 00:00:00 GMT-0300 (Horário Padrão de Brasília)','2019-04-08T03:00:00.000Z'),
sameDay('Sun Apr 07 2019 00:00:00 GMT-0300 (Horário Padrão de Brasília)','2019-04-13T03:00:00.000Z'),
);

How to check if two arrays of dates have matching items?

I have two arrays of dates. The first one have all the booked dates and the second one will have dates between "start day" and "end day" which user will pick.
Now I have to confirm that the days between the start and stop will not be found from the fully booked dates array.
I'm using Vue.js to update data.
Here is what I have done to get those dates:
/**
* Get dates from datepicker and format it to the same format that fully booked array has it's dates.
**/
var day1 = moment( this.startDate, 'DD/MM/Y').format('Y,M,D');
var day2 = moment( this.endDate, 'DD/MM/Y').format('Y,M,D');
var start = new Date(day1);
var end = new Date(day2);
/**
* Get dates between start and stop and return them in the dateArray.
**/
Date.prototype.addDays = function(days) {
var dat = new Date(this.valueOf());
dat.setDate(dat.getDate() + days);
return dat;
};
function getDates(startDate, stopDate) {
var dateArray = [];
var currentDate = startDate;
while (currentDate <= stopDate) {
dateArray.push(currentDate);
currentDate = currentDate.addDays(1);
}
return dateArray;
}
var dateArray = getDates(start, end);
/**
* Set dateArray in to Vue.js data.
**/
this.$set('daysBetweenStartStop', dateArray);
/**
* Get arrays of dates from the Vue.js data. calendar = Booked dates | matchingDays = Dates between start and stop.
**/
var calendar = this.fullDates;
var matchingDays = this.daysBetweenStartStop;
/**
* #description determine if an array contains one or more items from another array.
* #param {array} haystack the array to search.
* #param {array} arr the array providing items to check for in the haystack.
* #return {boolean} true|false if haystack contains at least one item from arr.
*/
var findIfMatch = function (haystack, arr) {
return arr.some(function (v) {
return haystack.indexOf(v) >= 0;
});
};
var matching = findIfMatch(calendar, matchingDays);
/**
* Check the results. If false we are good to go.
**/
if (matching){
alert('WE FOUND A MATCH');
} else {
alert('GOOD TO GO');
}
Arrays are in the following format:
var calendar = [
Sun Oct 02 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 09 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 16 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 23 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 30 2016 00:00:00 GMT+0300 (EEST)
]
var matchingDays = [
Fri Oct 28 2016 00:00:00 GMT+0300 (EEST),
Sat Oct 29 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 30 2016 00:00:00 GMT+0300 (EEST)
]
My problem is that even if those two arrays have exactly same dates they will still somehow be considered as a not identical. Any ideas how to get this working?
Your match function should look like this :
findIfMatch = function (haystack, arr){
var i = 0;//array index
var j = 0;//array index
while(j < arr.length && i < haystack.length){
cur_cal = Date.parse(haystack[i]);
cur_match = Date.parse(arr[j]);
if(cur_cal > cur_match){
j++;
}else if(cur_cal < cur_match){
i++;
}else{
return true;
}
}
return false;
}
To get matching records from two array use this
var calendar = [
Sun Oct 02 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 09 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 16 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 23 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 30 2016 00:00:00 GMT+0300 (EEST)
];
var matchingDays = [
Fri Oct 28 2016 00:00:00 GMT+0300 (EEST),
Sat Oct 29 2016 00:00:00 GMT+0300 (EEST),
Sun Oct 30 2016 00:00:00 GMT+0300 (EEST)
];
var newCalendar = [];
var newMatchingDays = []
$.map(calendar, function(date){
newCalendar.push(date.toString())
});
$.map(matchingDays, function(date){
newMatchingDays.push(date.toString())
});
var result = [];
$.map(newCalendar, function (val, i) {
if ($.inArray(val, newMatchingDays) > -1) {
result.push(val);
}
});
console.log(result);
Firstly you can't compare dates like that, Date is an object.
eg.
var d1 = new Date('2016-09-30');
var d1 = new Date('2016-09-30');
console.log(d1 === d2); // = false
You would need to loop the array and compare each item, rather than use indexOf.
or maybe use the Array.filter(). Or alternatively use and object as a lookup.
eg. If say you had ->
var calendar = {
"Sun Oct 02 2016 00:00:00 GMT+0300 (EEST)": true,
"Sun Oct 09 2016 00:00:00 GMT+0300 (EEST)": true,
"Sun Oct 16 2016 00:00:00 GMT+0300 (EEST)": true,
"Sun Oct 23 2016 00:00:00 GMT+0300 (EEST)": true,
"Sun Oct 30 2016 00:00:00 GMT+0300 (EEST)": true
};
if (calendar["Sun Oct 16 2016 00:00:00 GMT+0300 (EEST)"]) {
console.log("We have date");
}
Notice how I use a string representation for the date, this could be a number too, eg. from Date.getTime().
Here is using Array.filter, I don't think it's as fast a Object lookup's. But here we go.
var calendar = [
new Date('2016-09-01'),
new Date('2016-09-12'),
new Date('2016-09-10')
];
var finddate = new Date('2016-09-12');
var found = calendar.filter(function (a) { return a.getTime() === finddate.getTime();});
And if you don't mind using third party library, try underscore..
eg.
var days = [new Date('2016-09-01'), new Date('2016-09-10'), new Date('2016-09-30')];
var finddays = [new Date('2016-09-01'), new Date('2016-09-30')];
var found = _.intersectionWith(days, finddays,
function (a,b) { return a.getTime() === b.getTime(); });

creating a date range from 2 dates with linq.js and momentjs

I want to get all dates between a startDate and an endDate.
I wrap the startDate/endDate with moment() again to clone the start/endDate because they must not be changed.
But still the getDateRange gives me odd results about dates:
testCase.startDate = moment(new Date(2014, 0, 1));
testCase.endDate = moment(new Date(2014, 0, 27));
Although both dates are in 2014 I get a dateRange from december 2013 days?
Why is that?
function getDateRange(startDate, endDate) {
return Enumerable.range(0, moment(endDate).diff(moment(startDate), 'days') + 1)
.select(function (offset) {
return moment(startDate).add(offset, 'days')
})
.toArray();
}
UPDATE
Your query looks like it should work. Maybe you're interpreting the dates incorrectly. Remember, only the month starts at 0. Maybe you offset the year too when you looked at the values.
Here's an alternate way you can write the query:
function getDateRange(startDate, endDate) {
return Enumerable.Unfold(startDate, "moment($).add(1, 'd')")
.TakeWhile(function (d) { return d <= endDate; })
.ToArray();
}
Based on what I'm seeing in the comments, it appears you're using methods which mutates the dates. You'll either want to avoid using these methods or clone the date first and manipulate the clones.
// without cloning
var date1 = moment.utc([2014, 0, 1]);
console.log(String(date1)); // Wed Jan 01 2014 00:00:00 GMT+0000
var startOfDate1 = date1.startOf('week'); // mutated
console.log(String(date1)); // Sun Dec 29 2013 00:00:00 GMT+0000
// using moment()
var date2 = moment.utc([2014, 0, 1]);
console.log(String(date2)); // Wed Jan 01 2014 00:00:00 GMT+0000
var startOfDate2 = moment(date2).startOf('week'); // not mutated
console.log(String(date2)); // Wed Jan 01 2014 00:00:00 GMT+0000
// using clone()
var date3 = moment.utc([2014, 0, 1]);
console.log(String(date3)); // Wed Jan 01 2014 00:00:00 GMT+0000
var startOfDate3 = date3.clone().startOf('week'); // not mutated
console.log(String(date3)); // Wed Jan 01 2014 00:00:00 GMT+0000

Using a Date as the hash table key

How can I create a hash table object in JavaSript and use a date as the key? So far I've got this:
var eventHash = {};
for (var i = 0, l = events.length; i < l; i += 1) {
eventHash[events[i].date.getTime()] = events[i];
}
And then when I want to find the event associated with today I would use this:
var event = eventHash[(new Date(2011, 04, 26, 0, 0, 0, 0)).getTime()];
Can anyone see any pitfalls with this solution, or have any suggestions for improvement?
Why wouldn't you just use an ISO8601 representation of the date, so the key would be like 20110426. Creating a date object seems a bit inefficient.
It would also make debugging easier as the property names are more human readable, even if you add hhmmss also.
The only issue I see is that it's pretty limiting if you suddenly need to have multiple events with the same date. Otherwise it should be alright.
Also: Today is May 23, not Apr 26 :)
I have a somewhat similar problem and my solution may help others.
I have a list of "entries", each entry has a timestamp and a value.
I want to divide them up into "buckets", one for each day.
In Python I would have used collections.defaultdict but since JavaScript does not have something like that I do the following.
If you want to read the keys, remember that when you use a Date as an object key it gets converted as a string.
var get_entries = function() {
var entries = [];
entries.push({
'timestamp': 1381831606,
'value': 3
});
entries.push({
'timestamp': 1381831406,
'value': 2
});
entries.push({
'timestamp': 1381531606,
'value': 6
});
entries.push({
'timestamp': 1381221606,
'value': 9
});
entries.push({
'timestamp': 1381221601,
'value': 8
});
entries.push({
'timestamp': 1381221656,
'value': 7
});
return entries;
};
var normalize_date = function(timestamp) {
// JavaScript timestamps work with milliseconds.
var dt = new Date(timestamp * 1000);
return new Date(
dt.getFullYear(),
dt.getMonth(),
dt.getDate()
);
};
var prepare_data = function() {
var entry,
line = {};
var entries_raw = get_entries();
for (var i = 0; i < entries_raw.length; i++) {
entry = entries_raw[i];
entry.date = normalize_date(entries_raw[i].timestamp);
// If date not exists in line, create it.
console.log('Found entry for date', entry.date, 'with value', entry.value);
if (typeof(line[entry.date]) === 'undefined'){
line[entry.date] = 0;
}
line[entry.date] += entry.value;
}
console.log(line);
return line;
};
prepare_data();
Output:
$ nodejs diaryindex.js
Found entry for date Tue Oct 15 2013 00:00:00 GMT+0200 (CEST) with value 3
Found entry for date Tue Oct 15 2013 00:00:00 GMT+0200 (CEST) with value 2
Found entry for date Sat Oct 12 2013 00:00:00 GMT+0200 (CEST) with value 6
Found entry for date Tue Oct 08 2013 00:00:00 GMT+0200 (CEST) with value 9
Found entry for date Tue Oct 08 2013 00:00:00 GMT+0200 (CEST) with value 8
Found entry for date Tue Oct 08 2013 00:00:00 GMT+0200 (CEST) with value 7
{ 'Tue Oct 15 2013 00:00:00 GMT+0200 (CEST)': 5,
'Sat Oct 12 2013 00:00:00 GMT+0200 (CEST)': 6,
'Tue Oct 08 2013 00:00:00 GMT+0200 (CEST)': 24 }

Categories

Resources