Suppose I have an array of objects, each representing a certain time and a respective price value:
const prices = [
{
price_time: '2021-10-13T16:30:00 00:00',
price: 3.5,
},
{
price_time: '2021-10-13T18:15:00 00:00"',
price: 5,
},
{
price_time: '2021-10-13T19:15:00 00:00"',
price: 6.5,
},
];
Now assume I need to output a list of all prices with 15 minutes intervals for every hour existing between the price_time
values of the first and last elements of the prices
array.
The resulting intervals list for the defined 16:30
, 18:15
and 19:15
times will then be:
[
{ price_time: '2021-10-13T16:30:00.000Z', price: 3.5 }, // first elem in the prices array
{ price_time: '2021-10-13T16:45:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T17:00:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T17:15:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T17:30:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T17:45:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T18:00:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T18:15:00.000Z', price: 5 }, // second elem in the prices array
{ price_time: '2021-10-13T18:30:00.000Z', price: 5 },
{ price_time: '2021-10-13T18:45:00.000Z', price: 5 },
{ price_time: '2021-10-13T19:00:00.000Z', price: 5 },
{ price_time: '2021-10-13T19:15:00.000Z', price: 6.5 } // last elem in the prices array
]
What would be the most optimal way to achieve this? I have been trying to get it done by iterating the array values, getting the hours between each two elements and then filling in the 00:00
, 00:15
, 00:30
and 00:45
intervals:
const _ = require('date-fns');
const prices = [
{ price_time: '2021-10-13T16:30:00 00:00', price: 3.5 },
{ price_time: '2021-10-13T18:15:00 00:00',price: 5 },
{ price_time: '2021-10-13T19:15:00 00:00', price: 6.5 },
];
function addMissingIntervals() {
const allIntervals = [];
prices.forEach((item, index) => {
if (index !== prices.length - 1) { // if this isn't the last loop item
const start = _.parseISO(item.price_time); // the datetime of the current loop item
const end = _.parseISO(prices[index 1].price_time); // the datetime of the next loop item
const allHours = _.eachHourOfInterval({ start: start, end: end }); // all the hours between these two
allHours.map((hour) => {
allIntervals.push({ price_time: hour, price: item.price }) // insert the start of the hour
allIntervals.push(
...[15, 30, 45].map((t) => { // for each hour, add 15m, 30m & 45m
return { price_time: _.addMinutes(hour, t), price: item.price };
}),
);
});
}
});
// Adding the last item from `prices` as well
allIntervals.push(prices[prices.length - 1]);
return allIntervals;
}
console.log(addMissingIntervals());
The result of the latter, however, includes all intervals for all hours, including those preceding or following the start/end dates:
[
{ price_time: '2021-10-13T16:00:00.000Z', price: 3.5 }, // should not be included
{ price_time: '2021-10-13T16:15:00.000Z', price: 3.5 }, // should not be included
{ price_time: '2021-10-13T16:30:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T16:45:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T17:00:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T17:15:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T17:30:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T17:45:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T18:00:00.000Z', price: 3.5 },
{ price_time: '2021-10-13T18:15:00.000Z', price: 3.5 }, // should not be included
{ price_time: '2021-10-13T18:30:00.000Z', price: 3.5 }, // should not be included
{ price_time: '2021-10-13T18:45:00.000Z', price: 3.5 }, // should not be included
{ price_time: '2021-10-13T18:00:00.000Z', price: 5 }, // should not be included
{ price_time: '2021-10-13T18:15:00.000Z', price: 5 },
{ price_time: '2021-10-13T18:30:00.000Z', price: 5 },
{ price_time: '2021-10-13T18:45:00.000Z', price: 5 },
{ price_time: '2021-10-13T19:00:00.000Z', price: 5 },
{ price_time: '2021-10-13T19:15:00.000Z', price: 5 }, // should not be included
{ price_time: '2021-10-13T19:30:00.000Z', price: 5 }, // should not be included
{ price_time: '2021-10-13T19:45:00.000Z', price: 5 }, // should not be included
{ price_time: '2021-10-13T19:15:00.000Z', price: 6.5 }
]
CodePudding user response:
I managed to solve this by using a modification of this answer (which utilizes moment.js instead of date-fns).
Essentially, I am having a loop running between each two dates, rounding each loop item time to the nearest 15 minutes and assigning the right price for that interval. The last item in the original prices array is ignored and added to the resulting array at following the termination of the loop:
const _ = require('date-fns');
const prices = [
{ price_time: '2021-10-13T16:30:00 00:00', price: 3.5 },
{ price_time: '2021-10-13T18:15:00 00:00', price: 5 },
{ price_time: '2021-10-13T19:15:00 00:00', price: 6.5 },
];
function intervalsGroup(startDate, endDate, price) {
// Round to the nearest 15
let current = _.setMinutes(startDate, Math.ceil(_.getMinutes(startDate) / 15) * 15);
let group = [];
while (endDate > current) {
group.push({ price_time: current, price: price })
current = _.addMinutes(current, 15);
}
return group;
}
function addMissingIntervals() {
const allIntervals = [];
prices.forEach((item, index) => {
if (index !== prices.length - 1) { // if this isn't the last loop item
const start = _.parseISO(item.price_time); // the datetime of the current loop item
const end = _.parseISO(prices[index 1].price_time); // the datetime of the next loop item
allIntervals.push(...intervalsGroup(start, end, item.price)); // generate the 15m intervals between these two days
}
});
allIntervals.push(prices[prices.length - 1]);
return allIntervals;
}
console.log(addMissingIntervals());