Loop dates and add object if not excist in JSON data - javascript

I need to create calendar view with fullcalendar.io. To use calendar I need to create a JSON like this:
var db_data = [
{
"id": 5,
"user_id": 1,
"article_id": 5,
"title": "",
"start": "2016-03-25 15:18:46"
},
{
"id": 4,
"user_id": 1,
"article_id": 5,
"price": 55,
"title": "",
"start": "2016-03-15 15:18:46"
} etc.
I need to put price at calendar from every date from period_start to period_end, but for some prices I have values in database and for other I dont have so I need to use standar price (etc.$50)...
SO I have period_start and period_end and I get some data from database but now I need to create an JSON with objects for dates which are not in database so I create code:
function fulljson (){
var db_data;
$.ajax({
url: "http://localhost:8888/diving/{{$article->id}}",
type: "GET",
async: true,
dataType: "json",
success: function(data) {
db_data = data;
console.log(db_data);
// declare variables
var period_start = new Date('{{ date('Y-m-d', strtotime($article->from)) }}'),
period_end = new Date('{{ date('Y-m-d', strtotime($article->to)) }}'),
current_date = period_start,
array_of_all_dates = [];
// Create a populated array of dates
while (current_date.getTime() <= period_end.getTime()) {
array_of_all_dates.push(current_date);
current_date = new Date(+current_date);
current_date.setDate(current_date.getDate() + 1);
}
// Now loop over the array of populated dates and mutate, so something like
array_of_all_dates = array_of_all_dates.map(function (date) {
var found_in_db = db_data.filter(function (db_data) {
return new Date(db_data.start.replace(" ", "T")).getTime() === date.getTime(); // I need to do this comparison better!
});
if (found_in_db.length > 0) {
return found_in_db[0];
}
var new_object = {
title: '',
start: date,
price: '{{$article->price}}'
};
console.log(new_object);
return new_object;
});
console.log('result'+array_of_all_dates);
drawCalendar(array_of_all_dates);
},
error: function (data) {
console.log(data);
console.log('ERROR');
}
});
};
$(document).ready(function() {
fulljson();
});
function drawCalendar(data) {
$('#calendar').fullCalendar({
header: {
left: 'prev today',
center: 'title',
right: 'next'
},
defaultDate: Date.now(),
eventRender: function(event, element) {
element.find("fc-event-container").remove();
element.find(".fc-content").remove();
element.find(".fc-event").remove();
var new_description =
'<div style="display:inline-flex; float:right;"><div class="col-md-6"style="margin-top:5px";><p class="price">' + event.price + 'e</p></div>';
element.append(new_description);
} ,// allow "more" link when too many events
events: data,
loading: function(bool) {
$('#loading').toggle(bool);
}
}
});
};
And now this code work in Chrome:
But in FireFox I get this:
Please look at 1.2.3. February where I have data from database. You will see the changes.
SO why this code work in Chrome and dont work in Firefox? What is a problem? Is there a better solution to solve this problem?

SO why this code work in Chrome and dont work in Firefox?
Because the date strings provided are not valid and Firefox will transpose those to invalid date when passed to new Date(). Chrome however does parse them but this is not expected behavior cross browser
Send valid ISO strings from server or any format recommended in plugin docs
From fullcalender docs:
When specifying Event Objects for events or eventSources, you may
specify a string in IETF format (ex: "Wed, 18 Oct 2009 13:00:00 EST"),
a string in ISO8601 format (ex: "2009-11-05T13:15:30Z") or a UNIX
timestamp.

Related

Using JSON Array with AJAX

I'm trying to append parts of a JSON array (that is received from a server) to a table after clicking a button. Somehow I can't add the parts of the json array to this table.
This is the JSON Array received from the server:
{"pass": [
{
"Date":"01.01.2001",
"Time":"14:20",
"ID":"1234",
"Name":"Sat",
"elevation":"168.9°",
"losTime":"04:31"
},
{
"Date":"01.01.2002",
"Time":"14:30",
"ID":"1235",
"Name":"Com",
"elevation":"16.9°",
"losTime":"04:25"
}
]}
The Code is the following:
$(document).ready(function(){
$("#submit-button").click(function() {
$.ajax({
url: "../rest/passdata",
type: "GET",
data: {
satellite: document.getElementById("satellite").value,
startDate: document.getElementById("startDate").value,
startTime: document.getElementById("startTime").value,
endDate: document.getElementById("endDate").value,
endTime: document.getElementById("endTime").value
},
dataType: "json",
error: function() {
$('#info').html('<p>An error has occurred</p>');
},
success: function(response) {
var arr = response;
$("#pd-table").find("#pd-body").empty();
$.each(arr, function(i, value){
var checkbox = $('<input type="checkbox"/>');
$("#pd-table").find("#pd-body")
.append($("<tr>"))
.append($("<td>").append(checkbox))
.append($("<td>").append(arr.pass.ID[i]))
.append($("<td>").append(arr.pass.Date[i]))
.append($("<td>").append(arr.pass.Time[i]))
.append($("<td>").append(arr.pass.losTime[i]))
.append($("<td>").append(arr.pass.Name[i]))
.append($("<td>").append(arr.pass.elevation[i]))
.append($("</tr>"))
The checkbox gets added to the table, which makes me think that reading out the array does not work the way it should.
I already tried to parse the response from the server but that also didn't work out and in that case even the checkbox didn't get added to the table.
I hope someone can help me out!
Several problems:
You want to loop over the array in response.pass not the whole object
You are appending the cells to the <tbody> not to the new row.
You can not append a closing tag. The DOM only accepts complete elements and has no understanding of appending a closing tag in a jQuery object. The full element gets created when you do $('<tagname>')
Simplified version:
var arr = response.pass;
var $tbody = $("#pd-body").empty();
$.each(arr, function(i, value) {
var checkbox = $('<input type="checkbox"/>');
var $row = $("<tr>")
// append cells to the new row
.append($("<td>").append(checkbox))
.append($("<td>").text(value.ID))
.append($("<td>").text(value.Date))
.append($("<td>").text(value.Time))
.append($("<td>").text(value.losTime))
.append($("<td>").text(value.Name))
.append($("<td>").text(value.elevation));
// append complete row
$tbody.append($row)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="pd-table" border=1>
<tbody id="pd-body">
</tbody>
</table>
<script>
var response = {
"pass": [{
"Date": "01.01.2001",
"Time": "14:20",
"ID": "1234",
"Name": "Sat",
"elevation": "168.9°",
"losTime": "04:31"
},
{
"Date": "01.01.2002",
"Time": "14:30",
"ID": "1235",
"Name": "Com",
"elevation": "16.9°",
"losTime": "04:25"
}
]
}
</script>

Elasticsearch Node.js date operators in query search

I'm storing data in my local Node JS Elastic Search database, now I've inserted records and one of the fields is a created_at field, which I've stored with new Date(), however, upon retrieval of the data, it seems that these are Strings, and thus my query doesn't seem to return any results.
My data looks like:
{
"_index": "my_table",
"_type": "_doc",
"_id": "V1mouXcBt3ZsPNCwd3A1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"properties": {
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"updated_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
},
"data": {
"name": "performance_overview",
"friendly_name": "Performance Overview",
"data": {
"cached": "9:39:21am",
"from": "02/19/2021 00:00:00",
"to": "02/19/2021 23:59:59",
"success": true,
"message": "message",
"sessions": "265",
"leads": "123",
"conversion_rate": "46.42"
},
"created_at": "2021-02-19 09:02:16",
"updated_at": "2021-02-19 09:02:16"
}
}
}
I'm using Moment JS, and have formatted the dates, and attempted what I believe is accurate for field mappings, although I'm new to Elastic Search and this seems to not return any results either.
const from = moment(from).startOf('day')
const results = await elastic.find('my_table', {
query: {
range: {
created_at: {
gte: moment(from).format('YYYY-MM-DD HH:MM:SS'),
}
}
}
})
elastic.find is a custom function that I've written which looks like the following and is exported from another JS file:
const find = async (table, data) => {
try {
const found = await client.search({
index: table,
body: data
}, {
ignore: [404],
maxRetries: 3
})
return found.body
} catch (err) {
return err
}
}
Elasticsearch is a JSON-in, JSON-out interface that stores your datetime data as strings of a given format (could be numeric (milli)seconds since the epoch too, if you choose so.)
As to why new Date() was converted to a string — at some point before the network transport, the JS client will serialize your documents-containing request body by calling JSON.stringify on it. When this function is called on a JS object, the JS runtime looks for an implementation of the .toJSON method on that object and serialize the value returned by the this method. You can verify this by running:
const date = new Date();
console.assert(`"${date.toJSON()}"` === JSON.stringify(date))
Now, in your elastic.find('my_table', {...}) call you're attempting two things at once — setting the mapping and querying the index. That's not going to work.
You've got to define your mapping before you ingest any documents. When you do that, make sure your date fields have a proper format to prevent inconsistencies down the line:
{
"properties": {
"created_at": {
"type": "date",
"format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
}
}
}
After that add some documents to your table/index (through the index operation).
Then and only then will you be able to use your range query. Here's a good working example.

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

return conditional week days using reduceRight

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

DataTables Plugin: How to format a date column in DataTable plugin?

I am new in the DataTable Plugin for jquery.
I would like to convert a column date to dd/mm/yyyy but it is returning me in the table this format: Wed Jan 09 2013 00:00:00 GMT+0100 (Hora estándar romance).
$('#tblProceso').dataTable({
"data": dataSet.dataList,
"columns": [
{ "title": "my date", "data": "mydate" }
]
});
Class I am using for it is
Public class DateClass {
Public Property mydate As DateTime
}
this process is called in ajax function and in the response I assign the list of DateClass.
How can I format the column?
What do I need to add?
If you are returning a nullable date time the render function needs to work a little different:
$('#tblProceso').DataTable({
columns: [
{"title": "my date",
"data": "mydate",
"type": "date ",
"render":function (value) {
if (value === null) return "";
var pattern = /Date\(([^)]+)\)/;
var results = pattern.exec(value);
var dt = new Date(parseFloat(results[1]));
return (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear();}
}
]};
my approach using Moment.js based at David Sopkpo answer:
$('#tblProceso').DataTable({
columns: [
{"title": "my date",
"data": "mydate",
"type": "date ",
"render":function (value) {
if (value === null) return "";
return moment(value).format('DD/MM/YYYY');
}
}
]};
On method would be like this,
When you create date list at server side, store date in required format (dd/mm/yy). So when you make Ajax call and get list of date, it will came from server by default format you want.
*sorry for my bad English.
if you want to format it with javascript, you need to override of the fnRender function of the column definition. Something like this:
$("#tblProceso").dataTable({
"data": dataSet.dataList,
"aoColumnDefs": [
{
"aTargets": [0], //column index counting from the left
"sType": 'date',
"fnRender": function ( dateObj ) {
var oDate = new Date(dateObj.aData[0]);
result = oDate.getDate()+"/"+(oDate.getMonth()+1)+"/"+oDate.getFullYear();
return "<span>"+result+"</span>";
}
}
],
"columns": [
{ "title": "my date", "data": "mydate" }
]
});
with datatables if you have a column with dates, you will have issues when sorting, so, use this plugin

Categories

Resources