return conditional week days using reduceRight - javascript

i am trying to get specific week(such as saturday) days collection from my mongodb. here is the example DB collection
var array2 = [
{
"_id": "1",
"start": "2016-08-23T16:00:00.000Z",
},
{
"_id": "2",
"start": "2016-08-26T21:00:00.000Z",
},
{
"_id": "3",
"start": "2016-08-23T16:10:00.000Z",
}];
javascript week reference
to get Saturday entry i run a map, and here is the code
array2.map(function(object){
var newObject = {};
var nedatee = new Date(object.start);
var weekday = nedatee.getDay();
if (weekday===6) {
newObject['start'] = true;
}
return newObject;
});
the output is
[ {}, {'start':true}, {}]
i dont want the empty object in my array, and i dont want to manually remove it, is there any smarted way? how can i make good use of reduceRight ?
all i want is
[{'start':true}]
javascript map reference if you need

You could use reduceRight and splice the failures:
var array2 = [
{
"_id": "1",
"start": "2016-08-23T16:00:00.000Z",
},
{
"_id": "2",
"start": "2016-08-26T21:00:00.000Z",
},
{
"_id": "3",
"start": "2016-08-23T16:10:00.000Z",
}];
array2.reduceRight(function(acc,v,i) {
if (new Date(v.start).getUTCDay() != 6){
array2.splice(i,1);
}
},null)
console.log(array2);
Note that the date will be parsed as UTC, so 2016-08-26T21:00:00.000Z will be Saturday in any timezone that is UTC+0300 or more, hence use of getUTCDay (i.e. none of the days is Saturday in the GMT time zone).
Also, you should not really rely on parsing any date string with the Date constructor. You should use a parser and provide the format, see Chrome can't parse date but IE can.

Have you considered using javascript some? Your code would then be
var hasSaturdays = array2.some(function isSaturday(object){
var nedatee = new Date(object.start);
return nedatee.getDay() === 6;
});
Then if hasSaturdays is true then you can return [{'start':true}]
Please note that it is only IE 9+

Using UTC time zone or not this is a simple filtering work and there is Array.prototype.filter() just for this job.
var array2 = [
{
"_id": "1",
"start": "2016-08-23T16:00:00.000Z",
},
{
"_id": "2",
"start": "2016-08-26T21:00:00.000Z",
},
{
"_id": "3",
"start": "2016-08-23T16:10:00.000Z",
}],
saturdays = array2.filter(date => (new Date(date.start)).getDay() === 6)
.map(date => ({start:true}));
console.log(saturdays);

Related

Javascript: Removing Semi-Duplicate Objects within an Array with Conditions

I am trying to remove the "Duplicate" objects within an array while retaining the object that has the lowest value associated with it.
~~Original
var array = [
{
"time": "2021-11-12T20:37:11.112233Z",
"value": 3.2
},
{
"time": "2021-11-12T20:37:56.115222Z",
"value": 3.8
},
{
"time": "2021-11-13T20:37:55.112255Z",
"value": 4.2
},
{
"time": "2021-11-13T20:37:41.112252Z",
"value": 2
},
{
"time": "2021-11-14T20:37:22.112233Z",
"value": 3.2
}
]
~~Expected Output
var array = [
{
"time": "2021-11-12T20:37:11.112233Z",
"value": 3.2
},
{
"time": "2021-11-13T20:37:41.112252Z",
"value": 2
},
{
"time": "2021-11-14T20:37:22.112233Z",
"value": 3.2
}
]
What I have so far:
var result = array.reduce((aa, tt) => {
if (!aa[tt.time]) {
aa[tt.time] = tt;
} else if (Number(aa[tt.time].value) < Number(tt.value)) {
aa[tt.time] = tt;
}
return aa;
}, {});
console.log(result);
I realize the issue with what I am trying to do is that the "time" attribute is not identical to the other time values I am considering as duplicates.
Though for this use case I do not need the time out to ms. YYYY-MM-DDTHH:MM (to the minute) is fine. I am not sure how to implement a reduction method for this case when the time isnt exactly the same. Maybe if only the first 16 characters were checked in the string?
Let me know if any additional information is needed.
So a few issues:
If you want to only check the first 16 characters to detect a duplicate, you should use that substring of tt.time as key for aa instead of the whole string.
Since you want the minimum, your comparison operator is wrong.
The code produces an object, while you want an array, so you still need to extract the values from the object.
Here is your code with those adaptations:
var array = [{"time": "2021-11-12T20:37:11.112233Z","value": 3.2},{"time": "2021-11-12T20:37:56.115222Z","value": 3.8},{"time": "2021-11-13T20:37:55.112255Z","value": 4.2},{"time": "2021-11-13T20:37:41.112252Z","value": 2},{"time": "2021-11-14T20:37:22.112233Z","value": 3.2}];
var result = Object.values(array.reduce((aa, tt) => {
var key = tt.time.slice(0, 16);
if (!aa[key]) {
aa[key] = tt;
} else if (Number(aa[key].value) > Number(tt.value)) {
aa[key] = tt;
}
return aa;
}, {}));
console.log(result);

Postman/JavaScript/API: Check if string contains dates and convert into time stamp

I am completely new to the topic of APIs and Postman and have the following question:
How can I detect multiple dates in a string and reformat them into a timestamp?
I pulled a JSON format via an API, then converted it to a string via the JSON.stringify function, and then stored it in an environment variable.
It now looks something like this:
[{“date”:“2000-01-01”,“value”:11.8432},{“date”:“2001-01-01”,“value”:112.2348},{“date”:“2002-01-01”,“value”:182.3777},{“date”:“2003-01-01”,“value”:15.0186},{“date”:“2004-01-01”,“value”:131.3781},{“date”:“2005-01-01”,“value”:145.3683}]
Now I’m trying to get this string back into a JSON format but I’d like to add a UNIX time stamp to the date (in milliseconds since January 1, 1970).
So it should look something like this:
[{“date”:946684800000,“value”:11.8432},{“date”:978307200000,“value”:112.2348},{“date”:1009843200000,“value”:182.3777}…
Does anyone know how to solve this within Postman (JavaScript for Postman)?
use moment library and the valueOf() method:
moment = require('moment')
let date = [{ "date": "2000-01-01", "value": 11.8432 }, { "date": "2001-01-01", "value": 112.2348 }, { "date": "2002-01-01", "value": 182.3777 }, { "date": "2003-01-01", "value": 15.0186 }, { "date": "2004-01-01", "value": 131.3781 }, { "date": "2005-01-01", "value": 145.3683 }]
date = date.map((item) => {
item.date = moment(item.date, "YYYY-MM-DD").valueOf()
return item
});
console.log(date)

Javascript: Filter JSON by date - display past and future objects [duplicate]

This question already has answers here:
how to filter array of objects based upon timestamp
(2 answers)
Closed 3 years ago.
What I want to achieve is, to filter the JSON by past and future events from the current date - like now. My "past" API route should display only the past events, like "id":1 for this example.
Can I do that with, let's say, momentjs and lodash? What would be a correct approach?
This is my JSON array containing the datetime timestamp.
[
{
"id": 1,
"datetime": "2017-08-18T15:15:00+0200",
},
{
"id": 2,
"datetime": "2020-03-31T10:30:00+0200",
},
{
"id": 3,
"datetime": "2020-03-31T10:30:00+0200",
}
]
You can use the Date.parse() method to parse string dates and compare it with current time and filter accordingly as follows:
const data = [
{
"id": 1,
"datetime": "2017-08-18T15:15:00+0200",
},
{
"id": 2,
"datetime": "2020-03-31T10:30:00+0200",
},
{
"id": 3,
"datetime": "2020-03-31T10:30:00+0200",
}
];
const pastDates = data.filter(x => Date.parse(x.datetime) < new Date());
const futureDates = data.filter(x => Date.parse(x.datetime) > new Date());
console.log("Past dates", JSON.stringify(pastDates, null, 4));
console.log("Future dates", JSON.stringify(futureDates, null, 4));

Flattening two arrays of date ranges in JavaScript?

I am building an availability calendar. This is a monthly view, and days are essentially a boolean, they can be either available or unavailable. There are multiple "products" that can exist on the calendar. Relatively simple.
I store these "availability ranges" as an array of objects to be as terse as possible. So a possible data set for a single product looks like this:
[
{
"startDate": "2016-11-08",
"endDate": "2016-11-08"
},
{
"startDate": "2016-11-11",
"endDate": "2016-11-14"
},
{
"startDate": "2016-11-20",
"endDate": "2016-11-22"
}
]
The UI looks very similar to this calendar:
The real trouble comes when users update their availability. They have the choice to "update all" or "update one". For example, if Room 1 was already unavailable on January 5th, and the user now wants to make all rooms unavailable from January 1st to January 10th, I need to remove the Room 1 January 5th object from the array, because it overlaps with the new data.
Additionally, I'd like to merge any timespans that are contiguous, eg:
[
{
"startDate": "2016-11-08",
"endDate": "2016-11-08"
},
{
"startDate": "2016-11-09",
"endDate": "2016-11-09"
},
]
Should be merged to:
[
{
"startDate": "2016-11-08",
"endDate": "2016-11-09"
},
]
I realise this is a relatively complex question, but surely there must be a pre-existing solution, or at least something similar?
I have access to momentJs.
Here is a function (ES6) you could use to merge two arrays, each containing periods. I applied it below to some sample data which is a bit more extended than the data you provided, so it covers several cases of overlapping and adjacency:
function mergePeriods(a, b) {
return a.concat(b)
.sort( (a, b) => a.startDate.localeCompare(b.startDate) )
.reduce( ([res, end], p) =>
new Date(p.startDate).getTime()<=new Date(end).getTime()+90000000
? p.endDate > end
? [res, res[res.length-1].endDate = p.endDate]
: [res, end]
: [res.concat(p), p.endDate],
[[], '1970-01-01'])[0];
}
// sample data
var unavailable1 = [
{
"startDate": "2016-11-08",
"endDate": "2016-11-08"
},
{
"startDate": "2016-11-11",
"endDate": "2016-11-14"
},
{
"startDate": "2016-11-20",
"endDate": "2016-11-22"
},
{
"startDate": "2016-11-27",
"endDate": "2016-11-27"
},
{
"startDate": "2016-11-29",
"endDate": "2016-11-29"
}
];
var unavailable2 = [
{
"startDate": "2016-11-09",
"endDate": "2016-11-09"
},
{
"startDate": "2016-11-12",
"endDate": "2016-11-15"
},
{
"startDate": "2016-11-18",
"endDate": "2016-11-21"
},
{
"startDate": "2016-11-26",
"endDate": "2016-11-28"
}
];
// merge the sample data
var res = mergePeriods(unavailable1, unavailable2);
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Explanation
As a first step the two arrays are concatenated and sorted by increasing start date.
Then reduce is called on that array, with the start value (for the accumulator) equal to:
[[], '1970-01-01']
This pair consists of the array that will be accumulated to the final result (it starts empty), and the last encountered endDate date, which is set to a time long ago.
This pair is received in the reduce callback as [res, end], and the current element from the array is named p. Then some comparisons are made to detect how the period p relates to end. In case of overlap are adjacency, the previous element in the current result is updated (extended) to match the p.endDate, which also becomes the new value of end in the next iteration.
In case there is a complete inclusion of p in the previous period, it is just ignored, and [res, end] are retained as they are.
If the period p is disjoint from the previous one, it is concatenated to the result (with concat) and the end is set to this p.endDate.
When the result is composed that way, and reduce returns, we are no longer interested in the latest end date, but only the array, which explains the final [0].
About 90000000
The value 90000000 represents the number of milliseconds in 25 hours. This is to detect if two periods are adjacent. 1 hour extra does not hurt, and will deal well with overnight DST changes. This could alternatively be done with momentjs, but this is not too cumbersome in plain JavaScript either.
I don't know if this is the Fastest approach, but a good place to start would be to make use of the Array.sort to normalize the data by date so that any sequential dates will be next to each other, then use a simple array traversal to coalesce sequential dates.
The example below works with sort an array traversal.
let states = [
{
"startDate": "2016-11-08",
"endDate": "2016-11-08"
},
{
"startDate": "2016-11-11",
"endDate": "2016-11-14"
},
{
"startDate": "2016-11-20",
"endDate": "2016-11-22"
},
{
"startDate": "2016-11-15",
"endDate": "2016-11-19"
},
];
console.log('Started With:', states);
let sorted_states = states.sort(function(a,b){
return (new Date(a.startDate)) - (new Date(b.startDate))
});
// simple array traversal method
for (let i = sorted_states.length - 1; i--;){
let current = sorted_states[i];
let next = sorted_states[i+1];
interval = ((new Date(next.startDate) - new Date(current.endDate)));
if (interval <= (1000 * 60 * 60 * 24)){ // one day
newEntry = {
startDate : current.startDate,
endDate : next.endDate,
};
sorted_states[i] = newEntry;
sorted_states.splice(i+1, 1); // delete coalesced entry
}
}
console.log('Ended With:', sorted_states)
You could use two objects as hash table for start and end dates and use use moments for getting the date of tomorrow and look if the date is a start date.
Then update the object, delete from start and end object, and register again.
This proposal works with unsorted data.
function getTomorrow(day) {
return moment(day).add(1, 'days').format("YYYY-MM-DD");
}
var array = [{ startDate: "2016-11-08", endDate: "2016-11-08" }, { startDate: "2016-11-09", endDate: "2016-11-09" }, { startDate: "2016-12-02", endDate: "2016-12-02" }, { startDate: "2016-12-03", endDate: "2016-12-03" }, { startDate: "2016-12-01", endDate: "2016-12-01" }, { startDate: "2016-12-04", endDate: "2016-12-04" }, ],
start = {},
end = {},
result;
array.forEach(function (object) {
start[object.startDate] = object;
end[object.endDate] = object;
});
Object.keys(end).forEach(function (today) {
var tomorrow = getTomorrow(today);
if (start[tomorrow]) {
end[today].endDate = start[tomorrow].endDate;
end[tomorrow] = end[today];
delete end[today];
delete start[tomorrow];
}
});
result = Object.keys(start).map(function (k) {
return start[k];
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.2/moment.min.js"></script>
Use isbetween method from moment.
Design : after user selects a starting and end date , use his start date and compare it with each entry in your dates array (arr) if some entry has start date inside user selected range , delete it .After all entries are iterated upon , add user selected range to arr
userSdate="2016-11-08"
userEdate="2016-11-09"
for(i=0;i<arr.length;i++)
{
tmpSdate = arr[i].startDate;
tmpEdate = arr[i].endDate;
console.log( tmpSdate + " : " + tmpEdate );
isIn=moment(tmpSdate ).isBetween(userSdate,userEdate, null, '[]'); //note:[] means inclusive range
if(isIn)
{
delete(arr[i]);//remove arr[i]
}
}
arr.push({ "startDate": userSdate, "endDate" : userEdate});
Since delete doesn't reindex and update length property you might want to reindex it, though that's not mandatory.
Note that i have assumed there is no "range crossing" (when start of a range A lies completely inside range B but not it's(A) end does not) , see below
1 1
A-2------2----3----3--4----------4-----B (no range crossing)
C------D E----F G----------H
A--2------------------2--4--------------4---B (range crossing)
C-------3----------D G--3-----------H
E----------------F

Converting strings to intergers in JSON using JavaScript

I have the following json
{
"Title": "Test",
"StartDate": {
"Month": "3",
"Year": "1973"
},
"EndDate": {
"Month": "4",
"Year": "1974"
}
}
I want Month and Year values from StartDate and EndDate to be without quotes, like this:
{
"Title": "Test",
"StartDate": {
"Month": 3,
"Year": 1973
},
"EndDate": {
"Month": 4,
"Year": 1974
}
}
EDIT
I'm not creating the json, with JSON.stringify(). My JSON is created by Form Builder module from Angular 2, despite the fact that I'm setting it's type to number, after I change the value, the value gets quotes.
Before saving your JSON, use the parseInt() functions to convert your values into integers. This will remove the quotes.
JSON.stringify({
Month: parseInt( value , 10);
})
See this answer
EDIT
If you made that JSON Object earlier in your JavaScript code, go for #Adrian Lambertz's answer. If you got that JSON as a String from somewhere else and want to convert it, read my answer :
My original answer
Say you got this JSON as a string in your JavaScript code, you could convert the desired values to integers like this :
var events = JSON.parse(JSONStringYouWantToConvert);
// if the JSON String is an array of events that all have a Title, a StartDate and an EndDate
for (var i = 0; i < events.length; i++) {
// else, forget about the loop and the [i] index, the concept remains the same
events[i].StartDate.Month = parseInt(events[i].StartDate.Month);
events[i].StartDate.Year = parseInt(events[i].StartDate.Year);
events[i].EndDate.Month = parseInt(events[i].EndDate.Month);
events[i].EndDate.Year = parseInt(events[i].EndDate.Year);
}
// make a JSON String that wont have the quotes around the Month and Year numbers
var JSONStringConverted = JSON.stringify(events);
Just convert the strings to numbers.
This code is just an example, you can adapt it to whatever your object structure is.
function normalize(target) {
for (const date of ['StartDate', 'EndDate']) {
for (const item of ['Month', 'Year']) {
target[date][item] = Number(target[date][item]);
}
}
}
I can not see the order from which data the result is expeceted, so here two versions.
Object -> JSON (-> Object)
This works with JSON.stringify
and a replacer function for creating numbers for certain keys, like Month and Year.
var dataObj = { "Title": "Test", "StartDate": { "Month": "3", "Year": "1973" }, "EndDate": { "Month": "4", "Year": "1974" } },
jsonStr = JSON.stringify(dataObj, function (k, v) {
return ['Month', 'Year'].indexOf(k) !== -1 ? +v : v;
}),
parsed = JSON.parse(jsonStr);
console.log(jsonStr);
console.log(parsed);
JSON -> Object
This works with JSON.parse
and a reviver function for creating numbers for certain keys, like Month and Year.
var jsonStr = '{ "Title": "Test", "StartDate": { "Month": "3", "Year": "1973" }, "EndDate": { "Month": "4", "Year": "1974" } }',
parsed = JSON.parse(jsonStr, function (k, v) {
return ['Month', 'Year'].indexOf(k) !== -1 ? +v : v;
});
console.log(parsed);

Categories

Resources