Related
I have the following json file
[{"id":5,"num":"n61","mov_date":"2019-02-01T00:00:00","orders":19},
{"id":5,"num":"n61","mov_date":"2019-02-05T00:00:00","orders":12},
{"id":5,"num":"n61","mov_date":"2019-02-08T00:00:00","orders":5},
{"id":5,"num":"n61","mov_date":"2019-02-11T00:00:00","orders":7}]
I want to add new items using JavaScript, jquery, to end up with
[{"id":5,"num":"n61","mov_date":"2019-02-01T00:00:00","orders":19},
{"id":5,"num":"n61","mov_date":"2019-02-02T00:00:00","orders":0},
{"id":5,"num":"n61","mov_date":"2019-02-03T00:00:00","orders":0},
{"id":5,"num":"n61","mov_date":"2019-02-04T00:00:00","orders":0},
{"id":5,"num":"n61","mov_date":"2019-02-05T00:00:00","orders":12},
{"id":5,"num":"n61","mov_date":"2019-02-06T00:00:00","orders":0},
{"id":5,"num":"n61","mov_date":"2019-02-07T00:00:00","orders":0},
{"id":5,"num":"n61","mov_date":"2019-02-08T00:00:00","orders":5},
{"id":5,"num":"n61","mov_date":"2019-02-09T00:00:00","orders":0},
{"id":5,"num":"n61","mov_date":"2019-02-10T00:00:00","orders":0},
{"id":5,"num":"n61","mov_date":"2019-02-11T00:00:00","orders":7}]
maybe by calculating the number of missed items between dates, or just calculating the diff between the numbers represents the day i.e: "2019-02-01T00:00:00" and "2019-02-05T00:00:00" then add 3 items?
var items = [
{"id":5,"num":"n61","mov_date":"2019-02-01T00:00:00","orders":19},
{"id":5,"num":"n61","mov_date":"2019-02-05T00:00:00","orders":12},
{"id":5,"num":"n61","mov_date":"2019-02-08T00:00:00","orders":5},
{"id":5,"num":"n61","mov_date":"2019-02-11T00:00:00","orders":7}
]
var newItems = []
for(var i = 0; i < items.length; i++){
newItems.push(items[i])
var currentDay = moment(items[i].mov_date)
var nextDay = currentDay.add(1, 'days');
if(typeof items[i+1] !== 'undefined'){
var diff = moment(items[i+1].mov_date).diff(currentDay, 'days')
for(var j = 1; j <= diff; j++){
var newItem = JSON.parse(JSON.stringify(items[i]))
newItem.mov_date = moment(items[i].mov_date).add(j, 'days').utc(false).format();
newItem.orders = 0
newItems.push(newItem)
}
}
}
console.log(newItems)
<script src="https://cdn.jsdelivr.net/momentjs/2.13.0/moment.min.js"></script>
Check also this one that uses reduce() preserving all the props(including id) and only resetting orders and setting correct mov_date in between.
var items = [{
"id": 5,
"num": "n61",
"mov_date": "2019-02-01T00:00:00",
"orders": 19
},
{
"id": 5,
"num": "n61",
"mov_date": "2019-02-05T00:00:00",
"orders": 12
},
{
"id": 5,
"num": "n61",
"mov_date": "2019-02-08T00:00:00",
"orders": 5
},
{
"id": 5,
"num": "n61",
"mov_date": "2019-02-11T00:00:00",
"orders": 7
}
]
const newItems = items.reduce((acc, next) => {
// first run with early return
if (!acc.length) {
return [...acc, next]
}
// taking the recent item, to preserve the id and other props
const prevItem = acc[acc.length - 1];
// getting diff in days - 1
const days = moment.utc(next.mov_date).diff(moment.utc(prevItem.mov_date), 'days') - 1;
// [...Array] is a trick to get mappable arrays without array holes,
// but with initialized undefined values,
// so we can get the index during map
const inBetweenValues = [...Array(days)].map((_, dayIndex) => {
return {
...prevItem,
orders: 0,
mov_date: moment.utc(prevItem.mov_date).add(dayIndex + 1, 'days').format()
};
});
// merging it all, and moving to the next loop
return [...acc, ...inBetweenValues, next];
}, [])
console.log(newItems);
<script src="https://cdn.jsdelivr.net/momentjs/2.13.0/moment.min.js"></script>
You could use a Date object which you increment using its setDate method, and which you render to string with toJSON. Then when the date string matches the next entry, you copy it, otherwise you duplicate it with orders: 0:
const data = [{"id":5,"num":"n61","mov_date":"2019-02-01T00:00:00","orders":19},{"id":5,"num":"n61","mov_date":"2019-02-05T00:00:00","orders":12},{"id":5,"num":"n61","mov_date":"2019-02-08T00:00:00","orders":5},{"id":5,"num":"n61","mov_date":"2019-02-11T00:00:00","orders":7}];
const end = new Date(data[data.length-1].mov_date + "Z");
const result = [];
for (let dt = new Date(data[0].mov_date+"Z"), i = 0; dt <= end; dt.setUTCDate(dt.getUTCDate()+1)) {
result.push({...data[i], ...(dt.toJSON().slice(0,19) === data[i].mov_date ? (i++, {}) : { orders: 0 })});
}
console.log(result);
Here is an algorithm that will do That for you. I used
Convert JS date time to MySQL datetime for date conversion.
function twoDigits(d) {
if(0 <= d && d < 10) return "0" + d.toString();
if(-10 < d && d < 0) return "-0" + (-1*d).toString();
return d.toString();
}
function toMysqlFormat() {
return this.getUTCFullYear() + "-" + twoDigits(1 + this.getUTCMonth()) + "-" + twoDigits(this.getUTCDate()) + "T" + twoDigits(this.getUTCHours()) + ":" + twoDigits(this.getUTCMinutes()) + ":" + twoDigits(this.getUTCSeconds());
};
var prev = 0;
for( var x = 1; x < obj.length; x++ ){
if( !obj[x -1].mov_date ){
continue;
}
var tx = Date.parse( obj[x-1].mov_date );
var diff = ( Date.parse(obj[x].mov_date ) - tx ) / (1000*24*60*60);
for( var y = 1; y < diff; y++ ){
obj.splice( x - 1 + y,0, { "id" : 5, "num" : "n61", "mov_date" : toMysqlFormat.bind( new Date( tx + ( y*1000*24*60*60) ) )(), "orders" : 0} );
}
x += diff - 1;
}
for( var x = 0; x < obj.length; x++ ){
console.log( JSON.stringify( obj[x] ) );
}
/* Result :
/* {"id":5,"num":"n61","mov_date":"2019-02-01T00:00:00","orders":19} */
/* {"id":5,"num":"n61","mov_date":"2019-02-02T00:00:00","orders":0} */
/* {"id":5,"num":"n61","mov_date":"2019-02-03T00:00:00","orders":0} */
/* {"id":5,"num":"n61","mov_date":"2019-02-04T00:00:00","orders":0} */
/* {"id":5,"num":"n61","mov_date":"2019-02-05T00:00:00","orders":12} */
/* {"id":5,"num":"n61","mov_date":"2019-02-06T00:00:00","orders":0} */
/* {"id":5,"num":"n61","mov_date":"2019-02-07T00:00:00","orders":0} */
/* {"id":5,"num":"n61","mov_date":"2019-02-08T00:00:00","orders":5} */
/* {"id":5,"num":"n61","mov_date":"2019-02-09T00:00:00","orders":0} */
/* {"id":5,"num":"n61","mov_date":"2019-02-10T00:00:00","orders":0} */
/* {"id":5,"num":"n61","mov_date":"2019-02-11T00:00:00","orders":7} */
*/
Seen below is a time series bar graph with a range selector in plotly.js.
In it, I am trying to figure out how to group the values by week, but cannot seem to accomplish this. Is there a setting in plotly.js to group these by week when changing the time range selection? I cannot seem to figure out if it is possible.
Here are the main documentation pages they offer, of which I tried as many settings as I thought pertained to accomplishing this, but could not figure it out.
https://plot.ly/javascript/time-series/
https://plot.ly/javascript/bar-charts/
var days = (function(start,count){
var days = [];
var MSday = 1000 * 60 * 60 * 24;
for(var i = 0; i < count; i++){
days.push(new Date(+start + i*MSday));
}
return days;
})(new Date(2018,0,1),100);
function vals(){
var vals = [];
for(var i = 0; i < 100; i++){
vals.push((Math.random() * 2 * i) | 0);
}
return vals;
}
var selectorOptions = {
buttons: [{
step: 'month',
stepmode: 'backward',
count: 1,
label: '1m'
}, {
step: 'month',
stepmode: 'backward',
count: 6,
label: '6m'
}, {
step: 'year',
stepmode: 'todate',
count: 1,
label: 'YTD'
}, {
step: 'year',
stepmode: 'backward',
count: 1,
label: '1y'
}, {
step: 'all',
}],
};
var trace1 = {
x: days,
y: vals(),
type: 'bar',
name: 'Trace 1'
};
var trace2 = {
x: days,
y: vals(),
type: 'bar',
name: 'Trace 2'
};
var data = [trace1, trace2];
var layout = {
title: 'Bar Demo',
barmode: 'group',
xaxis: {
rangeselector: selectorOptions
}
};
Plotly.newPlot('myDiv', data, layout);
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"><!-- Plotly chart will be drawn inside this DIV --></div>
How can I make the 6 month selection group by week instead of by day on the graph?
Apparently this isn't built in. If it is, or becomes built in at some point, please indicate that here in a comment or another answer.
The only option I was able to determine as viable was to hook into the relayout event using .on('plotly_relayout', function () {, taking the arguments from the range selector buttons (which seem limited, only a from and to date, if there is a better way to determine the origination please also let me know and I will update here), and then roughly based on that to bin the dates by week and adjust the x and y values in the plot.
This is just a basic implementation as proof of concept. Using it in production would require refactoring this code to work with the existing data structures with regards to design and page implementation.
There is a lot going on here. Basically, it will iterate through the set of dates to create sunday bins which will hold the weekly data (note that it still lacks a display update to show it is a week from the start date). Once it has the bins it sums the dates in each bin range. Then it replaces the data set using restyle. If the range selected is not 6m then it will use the a slice of the backup data because plotly modifies arrays in place, and as a result it will overwrite the data if there is no backup copy in addition with a single copy every time the backup is used.
See below for a working demo.
function sum(array){
return array.reduce(function(sum,curr){
return sum + curr;
},0);
};
Date.MSday = 1000 * 60 * 60 * 24;
Date.prototype.floor = function(){
return new Date(this.getFullYear(),this.getMonth(),this.getDate());
}
Date.prototype.addDays = function(days){
var time = +this - +this.floor();
var addedDays = new Date(+this.floor() + Date.MSday*days);
return new Date(+addedDays + time);
}
function weeksFromDates(datesArray, valsArray){
var lastDay = datesArray[datesArray.length -1];
var firstDay = datesArray[0];
var dayOfWeek = firstDay.getDay();
var firstSunday = firstDay.addDays(-dayOfWeek);
var sundays = [];
var currentSunday = firstSunday;
while(currentSunday < lastDay){
sundays.push(currentSunday);
currentSunday = currentSunday.addDays(7);
}
currentSunday = currentSunday.addDays(7);
sundays.push(currentSunday);
var valSets = [];
var n = 0;
for(var i = 1; i < sundays.length; i++){
var last = sundays[i-1];
var next = sundays[i];
var theseVals = [];
for(; n < datesArray.length && last <= datesArray[n] && next > datesArray[n]; n++){
theseVals.push(valsArray[n]);
}
valSets.push(sum(theseVals));
}
sundays.pop();
return {x: sundays, y: valSets};
}
var MSday = 1000 * 60 * 60 * 24;
var days = (function(start,count){
var days = [];
for(var i = 0; i < count; i++){
days.push(new Date(+start + i*MSday));
}
return days;
})(new Date(2018,0,1),100);
function vals(){
var vals = [];
for(var i = 0; i < 100; i++){
vals.push((Math.random() * 2 * i) | 0);
}
return vals;
}
var selectorOptions = {
buttons: [{
step: 'month',
stepmode: 'backward',
count: 1,
label: '1m'
}, {
step: 'month',
stepmode: 'backward',
count: 6,
label: '6m'
}, {
step: 'year',
stepmode: 'todate',
count: 1,
label: 'YTD'
}, {
step: 'year',
stepmode: 'backward',
count: 1,
label: '1y'
}, {
step: 'all',
}],
};
var trace1 = {
x: days,
y: vals(),
type: 'bar',
name: 'Trace 1',
orientation: 'v'
};
var trace2 = {
x: days,
y: vals(),
type: 'bar',
name: 'Trace 2',
orientation: 'v'
};
var data = [trace1, trace2];
var dataBackup = $.extend(true,{},data);
var layout = {
title: 'Bar Demo',
barmode: 'group',
xaxis: {
rangeselector: selectorOptions
}
};
Plotly.newPlot('myDiv', data, layout);
$('#myDiv').on('plotly_relayout', function () {
var lower = new Date(arguments[1]['xaxis.range[0]']);
var upper = new Date(arguments[1]['xaxis.range[1]']);
var dayRange = (+upper - +lower) / MSday;
if( dayRange < 190 && dayRange > 170 ){
//6m
for(var n = 0; n < data.length; n++){
var weekly = weeksFromDates(dataBackup[n].x,dataBackup[n].y);
Plotly.restyle('myDiv',{x:[weekly.x],y: [weekly.y]},n);
}
}else{
for(var n = 0; n < data.length; n++){
Plotly.restyle('myDiv',{x:[dataBackup[n].x.slice()],y: [dataBackup[n].y.slice()]},n);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"><!-- Plotly chart will be drawn inside this DIV --></div>
Blimey! There is a much simpler option...
use 7 days:
step: 'day',
stepmode: 'backward',
count: 7,
label: '1w'
I have to make a bridge (script) between two databases (Mongo and Oracle).
I run three find queries on my MONGO database in three different collections.
Collection 1 = [{
name: 'Toto',
from: 'Momo',
note: 5,
cat: 'noCategori'
}]
Collection 2 = [{
city: 'london',
country: 'UK'
}]
...etc
I retrieve lists of documents from each collection I make a treatment on this list (Ex: average).
RowToSend = {
name: 'Toto',
note: 17, // average all document
city: 'london'
}
I establish a connection with Oracle and I persist this row.
I would like to run queries to mongo in parallel. at the end of the three requests and treatments. I compose my recording and I persist it on my database.
I have with async but it does not work. With await too. the function (Script) ends before the request is processed
function RequestOne(dateStart, dateEnd, RowToInsert) =
CollectionOne.find({
AQS_REF_TIME_EVENT_MSR: {
$gte: startInterv,
$lte: endInerv
},
})
.exec((err, arrAqsOneHourMoy) => {
if (err)
return err;
if (arrAqsOneHourMoy) {
AttributeOneAverge = 0;
AttributeTwoAverage = 0;
for (i = 0; i < arrAqsOneHourMoy.length; i++) {
AttributeOneAverge = (arrAqsOneHourMoy[i].AttributeOne + AttributeOneAverge);
AttributeTwoAverge = (arrAqsOneHourMoy[i].AttributeTwo + AttributeTwoAverge);
}
RowToInsert.AttributeOne = ((AttributeOneAverge) / (i + 1));
RowToInsert.AttributeOne = (AttributeTwoAverge) / (i + 1);
}
})
}
Function TWO .. same logic
Globale function :
function GenericFunction(time, sensorID, TEST_oracle_save) {
var d = new Date(2018, 00, 03, 11, 00, 00, 000);
var x = d.toISOString();
var d2 = new Date(2018, 00, 03, 11, 59, 59, 999);
var y = d2.toISOString();
RequestOne(x, y, TEST_oracle_save);
RequestTwo(x, y, TEST_oracle_save);
//Connexion to ORACLE DATABASE
//Connexion etablished
// Persist TEST_oracle_save on field Oracle
}
function RequestOne(dateStart, dateEnd, RowToInsert) =
CollectionOne.find({
AQS_REF_TIME_EVENT_MSR: {
$gte: startInterv,
$lte: endInerv
},
})
.exec((err, arrAqsOneHourMoy) => {
if (err)
return err;
if (arrAqsOneHourMoy) {
AttributeOneAverge = 0;
AttributeTwoAverage = 0;
for (i = 0; i < arrAqsOneHourMoy.length; i++) {
AttributeOneAverge = (arrAqsOneHourMoy[i].AttributeOne + AttributeOneAverge);
AttributeTwoAverge = (arrAqsOneHourMoy[i].AttributeTwo + AttributeTwoAverge);
}
RowToInsert.AttributeOne = ((AttributeOneAverge) / (i + 1));
RowToInsert.AttributeOne = (AttributeTwoAverge) / (i + 1);
}
})
}
Function TWO .. same logic
Globale function :
function GenericFunction(time, sensorID, TEST_oracle_save) {
var d = new Date(2018, 00, 03, 11, 00, 00, 000);
var x = d.toISOString();
var d2 = new Date(2018, 00, 03, 11, 59, 59, 999);
var y = d2.toISOString();
RequestOne(x, y, TEST_oracle_save);
RequestTwo(x, y, TEST_oracle_save);
//Connexion to ORACLE DATABASE
//Connexion etablished
// Persist TEST_oracle_save on field Oracle
}
I am trying to loop through an array of arrays with object [[{},{},{}],[{},{},{}]], and create a new array that basically "totals" the arrays. I am a bit at a loss on how to achieve this.
My data looks like this:
[
[{
"2017-01-05": 607
}, {
"2017-01-06": 430
}, {
"2017-01-07": 357
}, {
"2017-01-08": 277
}],
[{
"2017-01-09": 607
}, {
"2017-01-10": 430
}, {
"2017-01-11": 357
}, {
"2017-01-12": 277
}]
],
I would like to "count" and "label" each week, and total each week. Example:
newArray: [{"Week 1": 1075}, {"Week 2": 1590}]
I know I should probably use a forEach loop, but then it gets a bit sketchy:
dateArray.forEach( function (arrayWeek)
{
// push and name etc. functionality
});
I would greatly appreciate your assistance and guidance.
I would use the map function and reduce each week inside the map:
var days = [
[{"2017-01-05":607}, {"2017-01-06":430}, {"2017-01-07":357}, {"2017-01-08":277}],
[{"2017-01-09":607}, {"2017-01-10":430}, {"2017-01-11":357}, {"2017-01-12":277}]
];
function aggregator(memo, day) {
for (var i in day) {
memo += day[i];
}
return memo;
}
// Original version from the question
var weeks = days.map(function(week, index) {
var obj = {};
obj['Week ' + (index + 1)] = week.reduce(aggregator, 0);
return obj;
});
console.log(weeks);
// Follow up version from question in the comments
var weeks2 = days.map(function(week, index) {
return {
name: 'week ' + (index + 1),
total: week.reduce(aggregator, 0)
};
});
console.log(weeks2);
You can try something like this.
var data=[[{"2017-01-05":607},{"2017-01-06":430},{"2017-01-07":357},{"2017-01-08":277}],[{"2017-01-09":407},{"2017-01-10":430},{"2017-01-11":357},{"2017-01-12":277}]];
var result = data.reduce(function(p, c, i) {
var total = c.reduce(function(total, obj) {
for (var k in obj) {
total += obj[k];
}
return total;
}, 0);
// Format object in any format you want
var tmp = {};
tmp.name = "Week " + (i+1)
tmp.total = total;
// Push formatted object in array
p.push(tmp)
return p;
}, [])
console.log(result)
Note, I'd suggest you to use an object instead of array of objects. Benefit of this would be that you will not require to loop over output to get value. You can directly so result['week'+index]
var data=[[{"2017-01-05":607},{"2017-01-06":430},{"2017-01-07":357},{"2017-01-08":277}],[{"2017-01-09":407},{"2017-01-10":430},{"2017-01-11":357},{"2017-01-12":277}]];
var result = data.reduce(function(p, c, i) {
var total = c.reduce(function(total, obj) {
for (var k in obj) {
total += obj[k];
}
return total;
}, 0);
p["week" + (i + 1)] = total;
return p;
}, {})
console.log(result)
the variable weeks should hold what you want...
I'm assuming the week number is the actual week number in the year and not some index in the array. I'd also not use the same data structure but am adapting so that you won't need to change your structure.
var arr = [
[{
"2017-01-05": 607
}, {
"2017-01-06": 430
}, {
"2017-01-07": 357
}, {
"2017-01-08": 277
}],
[{
"2017-01-09": 607
}, {
"2017-01-10": 430
}, {
"2017-01-11": 357
}, {
"2017-01-12": 277
}]
]
function week(d) {
// taken from http://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php
d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
return Math.ceil((((d - new Date(d.getFullYear(), 0, 1)) / 8.64e7) + 1) / 7);
}
var weeks = {};
for (var x in arr) {
var subarr = arr[x]
for (var y in subarr) {
var obj = subarr[y];
for (var when in obj) {
var d = new Date(when);
var which_week = "Week " + week(d);
if (which_week in weeks) {
weeks[which_week] += obj[when];
} else {
weeks[which_week] = obj[when];
}
}
}
}
without forEach
var arrs = [
[{
"2017-01-05": 607
}, {
"2017-01-06": 430
}, {
"2017-01-07": 357
}, {
"2017-01-08": 277
}],
[{
"2017-01-09": 607
}, {
"2017-01-10": 430
}, {
"2017-01-11": 357
}, {
"2017-01-12": 277
}]
];
function loop1(arr, i, r){
r = (r ? r : 0) + arr[i][Object.keys(arr[i])[0]];
return ++i == arr.length ? r : loop1(arr, i, r);
}
function loop2(arrs, i, r){
i = i ? i : 0;
r = r ? r : {};
r['Week ' + (i + 1)] = loop1(arrs[i], 0);
return ++i == arrs.length ? r : loop2(arrs, i, r);
}
var newArr = loop2(arrs);
EDIT: Just to clarify, as zer00ne spotted this was originally minified code that I "beautified" in order to modify it. Apologies for not making that clear sooner.
Original for loop, that works:
m = 0;
for (k = b.header.length; m < k; m++) d.appendChild(p(e, "col", {
attr: {
min: m + 1,
max: m + 1,
width: N(b, m),
customWidth: 1
}
}));
What I want to do, but doesn't work:
m = 0;
for (k = b.header.length; m < k; m++) d.appendChild(p(e, "col", {
if (m==6) {
attr: {
min: m + 1,
max: m + 1,
width: 100,
style: 2,
customWidth: 1
}
} else {
attr: {
min: m + 1,
max: m + 1,
width: N(b, m),
customWidth: 1
}
}
}));
I understand that the if statement is in the wrong place w.r.t. the function d.appendChild, but I'm not sure how to separate the for loop from the function in order to place the if statement in the correct position. Finally what is this style of for loop/function hybridisation called?
Help?
It's not about for loop, it's about possibility to use if inside object literal. In short, there's none.
What you might do instead is extend a specific param based on condition, like this:
var m = 0;
var attr;
for (var k = b.header.length; m < k; m++) {
attr = {
min: m + 1,
max: m + 1,
customWidth: 1
};
if (m === 6) {
attr.style = 2;
attr.width = 100;
}
else {
attr.width = N(b, m);
}
d.appendChild(p(e, 'col', { attr: attr }));
}
This approach is DRY: the attributes common for both cases are specified once (so it's easier to modify them).
Have you worked with just a single varied param, there would have been another way to do this - with ternary:
attr = {
min: m + 1,
max: m + 1,
width: m === 6 ? 100 : N(b, m),
customWidth: 1
};
Still, there's another thing to consider: what's 6? It's not clear to any reader - including you several months (or even weeks, depending on the codebase's size) later.
Open the for loop, then put the if inside, and put appendChild in the if statement:
for (<stuff>) {
if (<expression>) {
d.appendChild(<stuff>);
}
}
You have put the if-condition as parameters into d.appendChild. That won't work:
m = 0;
for (k = b.header.length; m < k; m++) {
if (m==6) {
d.appendChild(p(e, "col", {
attr: {
min: m + 1,
max: m + 1,
width: 100,
style: 2,
customWidth: 1
}
}));
} else {
d.appendChild(p(e, "col", {
attr: {
min: m + 1,
max: m + 1,
width: N(b, m),
customWidth: 1
}
}));
}
}