javascript, how to change timestamp for elements in array? - javascript

I have array with this timestamps,
var labels = ["2018-12-01T00:00:00.000Z",
"2018-12-02T00:00:00.000Z",
"2018-12-09T00:00:00.000Z",
"2018-12-09T00:00:00.000Z",
"2018-12-18T00:00:00.000Z"
]
what is the best way to have array in format 2018-12-01. I need to have date for graph

You should use Array.prototype.map(), and create a new array with the characters you don't want sliced off the end:
var labels = ["2018-12-01T00:00:00.000Z",
"2018-12-02T00:00:00.000Z",
"2018-12-09T00:00:00.000Z",
"2018-12-09T00:00:00.000Z",
"2018-12-18T00:00:00.000Z"
];
var truncated = labels.map(str => str.slice(0, -14));
console.log(truncated);

Have you considered using moment?
function updateTimeFormat(arr) {
return arr.map(x => {
return moment(x).format('YYYY-MM-DD');
});
}

You can use the built-in Date, as so:
labels.map(label => {
const date = new Date(label);
return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
}

you can achieve what you want by using Array.prototype.map
var labels = ["2018-12-01T00:00:00.000Z",
"2018-12-02T00:00:00.000Z",
"2018-12-09T00:00:00.000Z",
"2018-12-09T00:00:00.000Z",
"2018-12-18T00:00:00.000Z"
];
const newDates = labels.map( label => {
const newd = new Date(label);
const fullYear = newd.getFullYear();
const month = (newd.getMonth() + 1 ).toString();
const date = (newd.getDate()).toString();
return `${fullYear}-${month.length > 1 ? month : month.replace(/^/,0)}-${date.length > 1 ? date : date.replace(/^/,0)}`;
});
console.log(newDates);

Related

JavaScript array of dates to array of ranges of dates

I have a JavaScript array of dates (as strings) like the following:
["2020-07-24T04:00:00.000Z", "2020-07-25T04:00:00.000Z", "2020-07-26T04:00:00.000Z", "2020-07-27T04:00:00.000Z", "2020-07-28T04:00:00.000Z", "2020-07-29T04:00:00.000Z", "2020-07-30T04:00:00.000Z", "2020-07-31T04:00:00.000Z", "2020-08-01T04:00:00.000Z", "2020-11-29T05:00:00.000Z", "2020-12-30T05:00:00.000Z", "2020-12-31T05:00:00.000Z", "2021-01-01T05:00:00.000Z", "2021-01-02T05:00:00.000Z", "2021-02-18T05:00:00.000Z"]
I want to convert this into an array of arrays of [first, last] contiguous date ranges, e.g., as below:
[["2020-07-24T04:00:00.000Z", "2020-08-01T04:00:00.000Z"], ["2020-11-29T05:00:00.000Z"], ["2020-12-30T05:00:00.000Z", "2021-01-02T05:00:00.000Z"], []]
How do I do this? Code attempt below:
var ranges = [];
for (var i = 0; i < popNull.length; i++) {
let currentRange = [];
let current = new Date(popNull[i]);
let tomorrow = new Date(current.getTime() + (24 * 60 * 60 * 1000));
let next = new Date(popNull[i+1]);
if (next === tomorrow) {
}
else {
}
}
I've made a couple of assumptions in the code below
That the dates are pre-sorted in ascending date order
That "contiguous" means less than or equal to 24 hours.
All dates are formatted in a way that can be passed directly to the Date constructor on the platform of choice.
const input = ["2020-07-24T04:00:00.000Z", "2020-07-25T04:00:00.000Z", "2020-07-26T04:00:00.000Z", "2020-07-27T04:00:00.000Z", "2020-07-28T04:00:00.000Z", "2020-07-29T04:00:00.000Z", "2020-07-30T04:00:00.000Z", "2020-07-31T04:00:00.000Z", "2020-08-01T04:00:00.000Z", "2020-11-29T05:00:00.000Z", "2020-12-30T05:00:00.000Z", "2020-12-31T05:00:00.000Z", "2021-01-01T05:00:00.000Z", "2021-01-02T05:00:00.000Z", "2021-02-18T05:00:00.000Z"].map(x => new Date(x));
let aggregation = input.reduce( (acc,i) => {
if(acc.prev){
const diffInHrs = (i - acc.prev)/1000/60/60;
if(diffInHrs <= 24){
acc.result[acc.result.length-1][1] = i;
}
else{
acc.result.push([i])
}
acc.prev = i;
return acc;
}
else{
return {prev:i, result:[[i]]}
}
},{});
console.log(aggregation.result)
You can reduce the dates by keeoing track of the latest and checking the current with the previous. You can diff their epoch valyes and check if they are within a day.
const dates = ["2020-07-24T04:00:00.000Z", "2020-07-25T04:00:00.000Z", "2020-07-26T04:00:00.000Z", "2020-07-27T04:00:00.000Z", "2020-07-28T04:00:00.000Z", "2020-07-29T04:00:00.000Z", "2020-07-30T04:00:00.000Z", "2020-07-31T04:00:00.000Z", "2020-08-01T04:00:00.000Z", "2020-11-29T05:00:00.000Z", "2020-12-30T05:00:00.000Z", "2020-12-31T05:00:00.000Z", "2021-01-01T05:00:00.000Z", "2021-01-02T05:00:00.000Z", "2021-02-18T05:00:00.000Z"];
const DAY_MILLIS = 8.64e7;
const ranges = dates
.reduce((acc, dateStr, index, all) => {
const dateObj = new Date(dateStr);
if (acc.length === 0) {
acc.push({ start: dateObj, prev: dateObj });
} else {
let last = acc[acc.length - 1];
const { start, prev } = last;
if (dateObj.getTime() - prev.getTime() <= DAY_MILLIS) {
last.prev = dateObj;
} else {
last.end = prev;
acc.push({ start: dateObj, prev: dateObj });
}
if (index === all.length - 1) {
last = acc[acc.length - 1];
if (last.end == null) {
last.end = last.prev;
}
}
}
return acc;
}, [])
.map(({ start, prev, end }) =>
((startStr, endStr) =>
startStr !== endStr ? [startStr, endStr] : [startStr])
(start.toISOString(), end.toISOString()));
console.log(ranges);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Output
[
[ "2020-07-24T04:00:00.000Z", "2020-08-01T04:00:00.000Z" ],
[ "2020-11-29T05:00:00.000Z" ],
[ "2020-12-30T05:00:00.000Z", "2021-01-02T05:00:00.000Z" ],
[ "2021-02-18T05:00:00.000Z" ]
]
You can do the following using Array#reduce():
Go through each date.
Check if the current date will extend last range.
if yes, then overwrite the end in the range pair (second element)
if no, start a new range
If it happens that a range only has a single date, then use the start to compare with. The logic still holds - extending the range will add a second date. If the new date is not within the desired time frame, then a new range is created and the previous range is left with a single element in it.
const areDatesWithin = ms => (str1, str2) => {
if (!str1 || !str2)
return false;
const date1 = new Date(str1);
const date2 = new Date(str2);
return (date2 - date1) <= ms;
}
const areDatesWithin1Day = areDatesWithin(1000 * 60 * 60 * 24);
function combineInRanges(dates) {
return dates.reduce((acc, nextDate) => {
const lastDateRange = acc[acc.length-1] ?? [];
//compare with range end (if there) or range start
const lastDate = lastDateRange[1] ?? lastDateRange[0];
//check if the range needs to be extended
const mergeWithRange = areDatesWithin1Day(lastDate, nextDate);
if (mergeWithRange) {
//change the end of the range
lastDateRange[1] = nextDate;
} else {
//start a new range
acc.push([nextDate]);
}
return acc;
}, []);
}
const arr = ["2020-07-24T04:00:00.000Z", "2020-07-25T04:00:00.000Z", "2020-07-26T04:00:00.000Z", "2020-07-27T04:00:00.000Z", "2020-07-28T04:00:00.000Z", "2020-07-29T04:00:00.000Z", "2020-07-30T04:00:00.000Z", "2020-07-31T04:00:00.000Z", "2020-08-01T04:00:00.000Z", "2020-11-29T05:00:00.000Z", "2020-12-30T05:00:00.000Z", "2020-12-31T05:00:00.000Z", "2021-01-01T05:00:00.000Z", "2021-01-02T05:00:00.000Z", "2021-02-18T05:00:00.000Z"];
console.log(combineInRanges(arr));
https://stackoverflow.com/a/67182108/20667780
Jamiec answer is working. If you have a date array with UTC dates correctly offsetted to local timezone, then the daylight save start/end date will have more than 24 hours. You have to change the diffInHrs to 25 instead of 24.
Otherwise, its a perfect answer.
It's a sort of reduction based on the even-ness of the index...
let array = ['a', 'b', 'c', 'd', 'e', 'f'];
let pairs = array.reduce((acc, el, idx) => {
idx % 2 ? acc[acc.length-1].push(el) : acc.push([el]);
return acc;
}, []);
console.log(pairs)

Simplest way to fill an array?

I need to create an array, and it's elements just depending on the index. To make sense about my current problem, I want to return the dates of a selected day's week.
// With for
const getWeekDatesFor = day => {
const dayIndex = moment(day).format('E') - 1;
const dates = [];
for(let i = 0; i < 7; i++){
if(i < dayIndex){
const lessWith = dayIndex - i;
dates[i] = moment(day).subtract(lessWith, "d").format('YYYY-M-D')
} else {
const moreWith = i - dayIndex;
dates[i] = moment(day).add(moreWith, "d").format('YYYY-M-D')
}
}
return dates
}
// How I wanted to simplify (but returns empty array with 7 elements)
const getWeekDatesNew = day => {
const dayIndex = moment(day).format('E') - 1;
return Array(7).map((e, i) => {
if(i < dayIndex){
const lessWith = dayIndex - i;
return moment(day).subtract(lessWith, "d").format('YYYY-M-D')
} else {
const moreWith = i - dayIndex;
return moment(day).add(moreWith, "d").format('YYYY-M-D')
}
})
}
For loops are good, but I'm sure that with ES6 we have a simpler way to perform these actions: create an array where items depending on it's index. All I want to know what is the ES6 method of doing this.
Simplest way is to use combination of array.from and new array like so:
Array.from(new Array(5), (und, ind) => ind) // This returns [0,1,2,3,4]
und is undefined since the array is initiated with undefined values and needs to be filled.
So you can extend this with more complex evaluating :
Array.from(new Array(5), (und, ind) => {return some_data_based_on_ind})
There is also Array.fill method for simple fills:
Array(6).fill('somedata')
Just push into your data variable. I have used something similar using date-fns instead of moment
const daysOfWeek = () => {
const days = []
let startDate = dateFns.startOfWeek(new Date())
for (let i = 0; i < 7; i++) {
days.push(
<span key={i}>
{ dateFns.format(dateFns.addDays(startDate, i), 'dddd') }
</span>
)
}
return days
}

JS: Get objects from array within a time range

There is an object array like this:
[
{
timestamp: 1318781876
any: 'other fields'
}
]
Of course there are multiple objects in that array. I am using momentJS - if it matters here...
Now I need to split that array into months. That means I need to get all objects for 'july' to display them in a table.
Is this possible at all or should I change the datastructure? I thought using timestamp is the best option, as I could calculate everything from this.
But now I'm thinking if I have to add month and year field to the object...
You could iterate the array and build up a tree, may also write year and month to the objects:
var map={};
array.forEach(function(obj){
var d = new Date(obj.timestamp*1000);
var m = obj.month = d.getMonth() +1;
var y = obj.year = d.getFullYear();
if(!map[y]) map[y]={};
if(!map[y][m]) map[y][m]=[];
map[y][m].push(obj);
});
So now weve got a map like this:
map: {
2017 : {
8 : [
{
timestamp:123456,
month:8,
year:2017,
any:"other value"
}
]
}
}
So you can now get all julys by:
map[2017][7]
It depends if you just do this once, then other answers will be easier, but if you need different timeranges the upper code just needs to iterate once, and you can get the filtered results easily. To get sorted results:
var sorted=Object.keys(map)/*the years*/ .sort().map(function(year){
return { year, months: Object.keys(map[year]).sort().map(function(month){
return {month,results:map[year][month]};
})
};
});
These arrays may already be built up while building the hash table, see ninas way of doing this
This can be done using array.filter
myArr = [
{
timestamp: 1318781876
any: 'other fields'
}
...
];
var filteredArray = myArr.filter(function(item) {
return (item.timestamp > minOfDateRange && item.timestamp < maxOfDateRange);
});
Your structure is quite useful. You can use Array.filter:
const startDate = new Date(2017, 6, 1); // 6 for July
const endDate = new Date(2017, 7, 1);
const selectedData = data.filter(entry => startDate <= entry.timestamp * 1000 && entry.timestamp * 1000 < endDate)
Convert the milisecond to a date using following code and get the month afterward
var date = new Date(milliseconds);
var month = date.getMonth();
then put the july object to another array and display them as you wish
You can use a hashmap approach.
var arr = [
...
{
timestamp: 1318781876
any: 'other fields'
}
...
];
var grouped = {};
var months = ['Jan', 'Feb' ...];
arr.forEach( function(item){
var dateObj = moment.unix(item.timestamp);
var month = months[dateObj.month()];
if(!grouped[month]){
grouped[month] = [];
}
grouped[month].push(item);
});
console.log(grouped);

What would be the best way to sort events according to its date

I have array with a few hundred events where every has date entry (in Date().getTime format in DB) and I have 10 days displayed in simple table. To every day-row I need to display number of events in that particular day. What would be the best way to do that?
Okay here is an update:
I've Firebase db with this structure:
{
events: {
event1: {
date: 144335265211,
title: "sometext",
text: "longer text"
},
event2: {
date: 1444482619766,
title: "sometext",
text: "longer text"
}
}
}
I am pulling it into local Redux store so I have an JS array with the same structure.
This way I am creating the table for particular days:
for (let dc = 0; dc < 10; dc++) {
const date = new Date();
date.setDate(date.getDate() + dc);
const m = date.getMonth();
const d = date.getDay();
const y = date.getUTCFullYear();
rows.push(
<td>"On the " + m + "/" + d + "/" + y + " is SOMENUMBER events"</td>
);
return rows;
}
Thanks a lot!
First you need to convert it to an array:
var events = Object.keys(obj).map((key) => {
return Object.assign({}, obj[key], {eventName: key});
});
Then you need to group them by date. We can do this with lodash.
var groupBy = require('lodash/collection/groupBy');
var eventsWithDay = events.map((event) => {
return Object.assign({}, event, {day: new Date(event.date).setHours(0, 0, 0, 0))
});
var byDay = groupBy(eventsWithDay, 'day');
And byDay will be an object with a key for each date with a value of an array of events for that day.

Counting array elements with specific date in javascript

I have an array of Date() objects in javascript and I want to count the number of events on each day.
Here is an example:
What I have is:
Array [ Date 2014-12-04T10:30:20.000Z, Date 2014-12-05T11:04:58.056Z, Date 2014-12-05T11:04:58.056Z, Date 2014-12-05T11:04:58.056Z ]
What I want is:
Array [{date: '2014-12-04', counts: 1}, {date: '2014-12-05', counts: 3}]
Thanks a lot!
Max
Basic answer:
var arr = [], // fill it with array with your data
results = {}, rarr = [], i, date;
for (i=0; i<arr.length; i++) {
// get the date
date = [arr[i].getFullYear(),arr[i].getMonth(),arr[i].getDate()].join("-");
results[date] = results[date] || 0;
results[date]++;
}
// you can always convert it into an array of objects, if you must
for (i in results) {
if (results.hasOwnProperty(i)) {
rarr.push({date:i,counts:results[i]});
}
}
These can be made much easier with lodash functions, and Array.forEach() in ES5
You much better off having a simple object with the keys as the date and the value as the count. I've added a simple pad function that prefixes a zero where the number is a single digit as per your output requirements.
function pad(n) {
return n.toString().length == 1 ? '0' + n : n;
}
function getCount(arr) {
var obj = {};
for (var i = 0, l = arr.length; i < l; i++) {
var thisDate = arr[i];
var day = pad(thisDate.getDate());
var month = pad(thisDate.getMonth() + 1);
var year = thisDate.getFullYear();
var key = [year, day, month].join('-');
obj[key] = obj[key] || 0;
obj[key]++;
}
return obj;
}
getCount(arr); // Object { 2014-04-12: 1, 2014-05-12: 3 }
DEMO
I came across the same issue and found this solution which uses Map()
`
calc = (obj) => {
const orders = []
const dates_map = new Map()
//iterate through all the objects inside the orders array
orders.forEach(order => {
// format and get the date
const date = new Date(order.created_at).toLocaleDateString('en-GB')
//check if the date key exists in the Map() and save it in a temp
const temp = dates_map.get(date) || false
// if it does not exist
if (temp) {
// clone the object
const previous = {...temp}
// increase counter
previous.count += 1
dates_map.set(date, previous)
}else{
//create new object to avoid overwriting
const result = {}
result.count = 1
dates_map.set(date, result)
}
})
console.log(dates_map)
}
And this is the output
Output: Map(3) {
'08/05/2021' => { count: 2 },
'09/05/2021' => { count: 1 },
'11/05/2021' => { count: 2,}
}
`

Categories