Sorting array of object javascript / typescript on firestore timestamp - javascript

I have below array structure
[
{
"id": "8gFUT6neK2I91HIVkFfy",
"element": {
"id": "8gFUT6neK2I91HIVkFfy",
"archived": false,
"updatedOn": {
"seconds": 1538653447,
"nanoseconds": 836000000
}
},
"groupBy": "pr"
},
{
"id": "9jHfOD8ZIAOX4fE1KUQc",
"element": {
"id": "9jHfOD8ZIAOX4fE1KUQc",
"archiveDate": {
"seconds": 1539250407,
"nanoseconds": 62000000
},
"archived": false,
"updatedOn": {
"seconds": 1538655984,
"nanoseconds": 878000000
}
},
"groupBy": "pr"
},
{
"id": "CeNP27551idLysSJOd5H",
"element": {
"id": "CeNP27551idLysSJOd5H",
"archiveDate": {
"seconds": 1539248724,
"nanoseconds": 714000000
},
"archived": false,
"updatedOn": {
"seconds": 1538651075,
"nanoseconds": 235000000
}
},
"groupBy": "pr"
},
{
"id": "Epd2PVKyUeAmrzBT3ZHT",
"element": {
"id": "Epd2PVKyUeAmrzBT3ZHT",
"archiveDate": {
"seconds": 1539248726,
"nanoseconds": 226000000
},
"archived": false,
"updatedOn": {
"seconds": 1538740476,
"nanoseconds": 979000000
}
},
"groupBy": "pr"
}
]
and below code to sort
Sample JSfiddle
http://jsfiddle.net/68wvebpz/
let sortedData = this.arraydata.sort((a:any, b:any) => { return Number(new Date(b.element.date).getTime()) - Number(new Date(a.element.date).getTime()) })
This does not make any effect.

There are a few problems that we need to fix:
Your updatedOn object is not something that can be converted to a date. You need to do extra work.
JavaScript doesn't support nanoseconds, only milliseconds. You will therefore need to divide that number by a million.
By using getTime for the comparison, you're actually discarding the milliseconds - that function returns seconds.
To fix the first two, use this function:
function objToDate(obj) {
let result = new Date(0);
result.setSeconds(obj.seconds);
result.setMilliseconds(obj.nanoseconds/1000000);
console.log('With nano', result);
return result;
}
This creates a new date and then sets the seconds and milliseconds. This gives you dates in October 2018 when I use your test data.
Then, to compare them and fix the remaining problems, use this (much simpler) form:
let sortedData = data.sort((a:any, b:any) => {
let bd = objToDate(b.element.updatedOn);
let ad = objToDate(a.element.updatedOn);
return ad - bd
});
That should do it.
To reverse the sort order, just use the less-than operator:
return bd - ad

Turn your strings into dates, and then subtract them to get a value that is either negative, positive, or zero:
array.sort(function(a,b){
return new Date(b.date) - new Date(a.date);
});

Is it something like this:
var array = [
{id: 1, name:'name1', date: 'Mar 12 2012 10:00:00 AM'},
{id: 2, name:'name2', date: 'Mar 8 2012 08:00:00 AM'}
];
console.log(array.sort((a, b) => {
return new Date(a.date) - new Date(b.date)
}))

Related

How to filter JSON data from API by date range in JavaScript

I want to filter the below JSON data by start date and end date, it should return the data between start date and end date, I tried to achieve using below code, but I'm doing wrong something to filter. I'm new to front end technologies like JavaScript. It would be appreciated if someone can correct me what I'm doing wrong here:
The API data is like that seen below
{
"rec_id": 1,
"emp_id": 1,
"date": "Jan 22, 2020",
"time_in": "09:20",
"time_out": "19:56",
"total_hours": 10.6,
"weekday": 4,
"name": "Carlina Dahlberg",
"gender": "Female",
"designation": "Supervisor",
"department": "Production",
"calculate": "",
"basic_salary": 20000,
"per_day_salary": 1000
},
{
"rec_id": 2,
"emp_id": 2,
"date": "Jan 22, 2020",
"time_in": "08:33",
"time_out": "13:16",
"total_hours": 4.72,
"weekday": 4,
"name": "Brenden Greenacre",
"gender": "Male",
"designation": "Executive",
"department": "Marketing",
"calculate": "",
"basic_salary": 25000,
"per_day_salary": 1250
},
This is my code file
async function getData(){
let myData = await fetch("http://34.198.81.140/attendance.json")
.then((respose) => {
return respose.json()
})
.then((data) => {
return data;
});
let startDate ="Feb 1, 2020";
let endDate = "Feb 29, 2020";
let result = myData.filter((data) => {
return data.date >= startDate && data.date <=endDate;
})
console.log(result);
}
getData()
The data get filtered but not as per requirement, please see the screen shot of console output. In the screen shot the Data come from 1 feb to 29 Feb but, the Data from 2 Feb to 9 Feb whole data is skip by filter function.
You can use Urnary (+) operator, with new Date() to create a timestamp, and then you can compare the timestamps to get desired result.
async function getData() {
/*
let myData = await fetch("http://34.198.81.140/attendance.json")
.then((respose) => {
return respose.json()
})
.then((data) => {
return data;
});*/
// For Testing Purposes only (data modified)
let myData = [{
"rec_id": 1,
"emp_id": 1,
"date": "Jan 22, 2020",
"time_in": "09:20",
"time_out": "19:56",
"total_hours": 10.6,
"weekday": 4,
"name": "Carlina Dahlberg",
"gender": "Female",
"designation": "Supervisor",
"department": "Production",
"calculate": "",
"basic_salary": 20000,
"per_day_salary": 1000
},
{
"rec_id": 2,
"emp_id": 2,
"date": "Feb 5, 2020",
"time_in": "08:33",
"time_out": "13:16",
"total_hours": 4.72,
"weekday": 4,
"name": "Brenden Greenacre",
"gender": "Male",
"designation": "Executive",
"department": "Marketing",
"calculate": "",
"basic_salary": 25000,
"per_day_salary": 1250
}
]
let startDate = +new Date("Feb 1, 2020");
let endDate = +new Date("Feb 29, 2020");
let result = myData.filter((data) => {
return +new Date(data.date) >= startDate && +new Date(data.date) <= endDate;
})
console.log(result);
}
getData()
You need to parse the date into something you can compare dates with.
There are a few options in front of you for that.
Rely on Date.Parse(), a bad idea ref. (mdn)
Use an alternative date library. A simple google search should reveal some.
After parsing the date, you can use the same logic that you are currently using.
you can do it through converting the strings of number into numbers. afterwards try comparing them with each other.
You can use JS libraries such as Moment.js or Dayjs as well to get around it.look through their APIs to dispel the snag you got stuck.
You can use the code below, if you decide to use async/await:
async function getData() {
const response = await fetch('http://34.198.81.140/attendance.json')
const myData = await response.json()
let startDate = 'Feb 1, 2020'
let endDate = 'Feb 29, 2020'
let result = myData.filter(data => {
return (
// Convert all date values to javascript dates using new Date(value)
// Get the number of milliseconds using getTime()
// Compare the milliseconds values
new Date(data.date).getTime() >= new Date(startDate).getTime() &&
new Date(data.date).getTime() <= new Date(endDate).getTime()
)
})
console.log(result)
}
getData()

Sort max date from Object

Trying to sort the object with the max date. One id may have multiples dates. Below is the format of the object where id:123 has two dates. So I am trying to take the max date for the user 123. I used the sort method and storing the array[0] but still there is something missing.
var arr = [
{
"scores": [
{
"score": 10,
"date": "2021-06-05T00:00:00"
}
],
"id": "3212"
},
{
"scores": [
{
"score": 10,
"date": "2021-06-05T00:00:00"
},
{
"score": 20,
"date": "2021-05-05T00:00:00"
}
],
"id": "123"
},
{
"scores": [
{
"score": 5,
"date": "2021-05-05T00:00:00"
}
],
"id": "321"
}
]
What I tried is
_.each(arr, function (users) {
users.scores = users.scores.filter(scores => new Date(Math.max.apply(null, scores.date)));
return users;
});
Expecting the output to look like the following with the max date selected.
[
{
"scores": [
{
"score": 10,
"date": "2021-06-05T00:00:00"
}
],
"id": "3212"
},
{
"scores": [
{
"score": 10,
"date": "2021-06-05T00:00:00"
}
],
"id": "123"
},
{
"scores": [
{
"score": 5,
"date": "2021-05-05T00:00:00"
}
],
"id": "321"
}
]
Your filter callback function is not performing a comparison to filter the correct element. Also, although applying the "maximum" algorithm on the dates as string would be fine in your case (because of the date format you have), it would be much safer to transform the date strings into date objects to consistantly get correct results regardless of the format.
In the solution below, you can use a combination of Array.map() and Array.sort() to copy and process your data in the correct result.
const data = [{
'scores': [{
'score': 10,
'date': '2021-06-05T00:00:00'
}],
'id': '3212'
}, {
'scores': [{
'score': 10,
'date': '2021-06-05T00:00:00'
}, {
'score': 20,
'date': '2021-05-05T00:00:00'
}],
'id': '123'
}, {
'scores': [{
'score': 5,
'date': '2021-05-05T00:00:00'
}],
'id': '321'
}];
// map the data and return the updated objects as the result
const result = data.map((user) => {
// copy the scores array to not mutate the original data
const sortedScores = user.scores.slice();
// sort the scores array by date descending
sortedScores.sort((a, b) => (new Date(b.date) - new Date(a.date)));
// return the same user with the first score from the sorted array
return {
...user,
scores: [sortedScores[0]]
};
});
console.log(result);

Rename keys in grouped array to represent the date they are grouped by

I have an array that I have grouped by 1 minute, relative to the rest of the date, except for seconds. The problem now is that the keys of each group in the array (3 groups) are just named 0, 1, 2 etc. and I want them to be named to the date they represent which they have been grouped by already.
Like so:
[
"Fri Jan 31 2020 14:58": [
{
"_id": "5e34326b1da7e21c04ec76e8",
"message": "15",
"room": "5e32c3f858f00d4ef5f9ab81",
"createdAt": 1580479083,
"user": "5e10e7a6a69a4a36e4169bdc",
"__v": 0
},
{
"_id": "5e34327e1da7e21c04ec76e9",
"message": "hello",
"room": "5e32c3f858f00d4ef5f9ab81",
"createdAt": 1580479102,
"user": "5e10e7a6a69a4a36e4169bdc",
"__v": 0
},
{
"_id": "5e34328c1da7e21c04ec76ea",
"message": "156",
"room": "5e32c3f858f00d4ef5f9ab81",
"createdAt": 1580479116,
"user": "5e10e7a6a69a4a36e4169bdc",
"__v": 0
}
]
]
See the code snippet, how can I achieve this?
const messages = [{"_id":"5e34326b1da7e21c04ec76e8","message":"15","room":"5e32c3f858f00d4ef5f9ab81","createdAt":1580479083,"user":"5e10e7a6a69a4a36e4169bdc","__v":0},{"_id":"5e34327e1da7e21c04ec76e9","message":"hello","room":"5e32c3f858f00d4ef5f9ab81","createdAt":1580479102,"user":"5e10e7a6a69a4a36e4169bdc","__v":0},{"_id":"5e34328c1da7e21c04ec76ea","message":"156","room":"5e32c3f858f00d4ef5f9ab81","createdAt":1580479116,"user":"5e10e7a6a69a4a36e4169bdc","__v":0},{"_id":"5e344bbc97cbc523d46acfad","message":"Newer","room":"5e32c3f858f00d4ef5f9ab81","createdAt":1580485564,"user":"5e10e7a6a69a4a36e4169bdc","__v":0},{"_id":"5e344e3a744b240a5cf7c9b5","message":"Newest ","room":"5e32c3f858f00d4ef5f9ab81","createdAt":1580486202,"user":"5e10e7a6a69a4a36e4169bdc","__v":0},{"_id":"5e344e3e744b240a5cf7c9b6","message":"Newest 1","room":"5e32c3f858f00d4ef5f9ab81","createdAt":1580486206,"user":"5e10e7a6a69a4a36e4169bdc","__v":0}];
const sortByDate = _.chain(messages)
.groupBy(m => {
const d = new Date(m.createdAt * 1000);
console.log(d);
return Math.floor(+(d) / (1000*60));
})
.sortBy((v, k) => { return k; })
.value();
console.log(sortByDate);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js" type="text/javascript"></script>
I managed to solve it with just _.groupBy and momentjs.
const sortByMinute = _.groupBy(messages, function (date) {
return moment(date.createdAt*1000).startOf("minute").format();
});
It groups them by minute and the returned object is named accordingly!
You could achieve that as this:
messages.map((message) => {
d = new Date(message.createdAt);
d.setMinutes(0);
d.setSeconds(0);
arr.push({[d]:message});
});
Hope it helps :)

How to generate an array of unique objects from api?

I'm trying to disable booked times from calendar, depending on the date. My goal is to create an array, which holds objects with single date and array of booked times.
I have created an api, which outputs something like this:
"bookings": [
{
"_id": "5ce1b8792598adasf452",
"workType": "Nail polishing",
"client": "Mary Johnson",
"date": "2019-05-31T00:00:00.000Z",
"bookingTime": "09:00"
},
{
"_id": "5ce1b8753hs53gasf452",
"workType": "Makeup",
"client": "Kate Bush",
"date": "2019-05-31T00:00:00.000Z",
"bookingTime": "10:00"
}
]
I've tried using Sets, filters, but I just can't seem to wrap my head around how to implement it to my own code.
Snippet of my code:
bookedTimes: []
fetchBookedTimes() {
axios.get("http://localhost:8080/api/bookings").then(res => {
for (var i = 0; i < res.data.bookings.length; i++) {
this.bookedTimes.push({
date: moment(res.data.bookings[i].date).format("YYYY-MM-DD"),
times: [res.data.bookings[i].bookingTime.substring(0,2)]
});
}
});
}
I expect the output to be
bookedTimes: [
{
date: "2019-05-31",
times: ["09", "10"]
},
{
date: "2019-06-01",
times: ["10", "11"]
}
]
But the actual output is
bookedTimes: [
{
date: "2019-05-31",
times: ["09"]
},
{
date: "2019-05-31",
times: ["10"]
},
{
date: "2019-06-01",
times: ["10"]
},
{
date: "2019-06-01",
times: ["11"]
}
]
As per the code, the actual output is correct. You are looping the response and pushing the data to an array. If you want to group them by date, then you have to create an object and then convert it to the expected output.
var result = res.data.bookings.reduce(function (defaultValue, booking) {
var date = moment(booking.date).format("YYYY-MM-DD");
defaultValue[date] = defaultValue[date] || {date: date, times: []};
defaultValue[date].times.push(booking.bookingTime.substring(0,2));
return defaultValue;
}, {});
console.log(Object.values(result));
You can simply use reduce()
const arr = [
{
"_id": "5ce1b8792598adasf452",
"workType": "Nail polishing",
"client": "Mary Johnson",
"date": "2019-05-31T00:00:00.000Z",
"bookingTime": "09:00"
},
{
"_id": "5ce1b8753hs53gasf452",
"workType": "Makeup",
"client": "Kate Bush",
"date": "2019-05-31T00:00:00.000Z",
"bookingTime": "10:00"
},
{
"_id": "5ce1b8753hs53gasf452",
"workType": "Makeup",
"client": "Kate Bush",
"date": "2019-06-31T00:00:00.000Z",
"bookingTime": "11:00"
},
{
"_id": "5ce1b8753hs53gasf452",
"workType": "Makeup",
"client": "Kate Bush",
"date": "2019-06-31T00:00:00.000Z",
"bookingTime": "12:00"
}
]
const res = arr.reduce((ac,{date,bookingTime}) => {
ac[date] = ac[date] || {date,bookingTime:[]}
ac[date].bookingTime.push(bookingTime.slice(0,2));
return ac;
},{})
console.log(Object.values(res))
You're pushing values directly into array but you need to group them by date so you can use an object and then push values to array in the end
Here temp is used to group values by date
We check for date it exists we push the time value to times array if not we create a new property on temp
In the end we push values to this.bookedTimes array
fetchBookedTimes() {
axios.get("http://localhost:8080/api/bookings").then(res => {
let temp = {}
for (var i = 0; i < res.data.bookings.length; i++) {
let date = moment(res.data.bookings[i].date).format("YYYY-MM-DD"),
let time = [res.data.bookings[i].bookingTime.substring(0,2)]
temp[date] = temp[date] || {date: date, times:[]}
temp[date].times.push(time)
});
}
this.bookedTimes.push(Object.values(temp))
});
}
First, check if the date of already in the array. Check if 'times' already exist in 'object.times', if not, push it to the 'object.times' array.
Please see the code below.
const date = moment(res.data.bookings[i].date).format("YYYY-MM-DD");
const times = res.data.bookings[i].bookingTime.substring(0, 2);
const arrayIndex = bookedTimes.findIndex(item => item.date === date);
//Check if date already exist in array
if (arrayIndex !== -1) {
//Check if 'times' already exist in 'object.times'
if (!bookedTimes[arrayIndex].times.includes(times)) {
//Push 'times' in 'object.times'
bookedTimes[arrayIndex].times.push(times);
}
} else {
//Push a new object into the array
bookedTimes.push({
date: date,
times: [times]
});
}

Javascript: group by with aggregation

I have a simple json list like the one below
{
"myList": [
{
"endOfPeriod": 1461362400000,
"rate": 0.03726378
},
{
"endOfPeriod": 1461535200000,
"rate": 0.03726378
},
{
"endOfPeriod": 1461967200000,
"rate": 0.03708314
},
{
"endOfPeriod": 1461708000000,
"rate": 0.03492851
},
{
"endOfPeriod": 1461794400000,
"rate": 0.03845068
},
{
"endOfPeriod": 1461621600000,
"rate": 0.03544827
}
]
}
Where endOfPeriod is a unix epoch timestamp. All the timestamps in the example belong to the same month (April 2016), but could be some other periods.
Assuming that I have already converted this json list into an array, and each unix timestamp into a DD.MM.YYYY date (I can keep them in unix timestamp too). Is there an efficient way to create a new array with the most recent rate for grouped by month/year?
I have to write code in Javascript.
For instance:
20.04.2016 / 0.33
21.04.2016 / 0.55
14.04.2016 / 0.88
02.05.2016 / 1.33
01.05.2016 / 5.44
New array must contain:
21.04.2016 / 0.55
02.05.2016 / 1.33
Thanks for your help.
If I understand correctly, you want to extract the most recent rate for each month. I would use lodash.
_.chain(arr)
.groupBy(function(item) {
var date = new Date(item.endOfPeriod);
return date.getFullYear() + '-' + date.getMonth();
})
.map(function(group) {
return _.maxBy(group, function(item) {
return item.endOfPeriod;
});
})
.value()
We start with a list of objects in the form:
{
"endOfPeriod" : 1464818400000,
"rate" : 0.05
}
The chain() function wraps the list into a lodash object.
Then, we group elements by year and month. After the groupBy(), we have the following structure (note that getMonth() is 0-based in Javascript, hence a value of 3 corresponds to April, and so on):
{
"2016-3" : [array of objects in April 2016],
"2016-4" : [array of objects in May 2016]
...
}
Then, for each group, we take the item with maximum endOfPeriod.
Finally, value() unwraps the lodash object back into a plain Javascript array.
Here is a result without using lodash. But for me it's better not to reinvent the wheel.
const myList = [
{
"endOfPeriod": 1461362400000,
"rate": 0.03726378
},
{
"endOfPeriod": 1461535200000,
"rate": 0.03726378
},
{
"endOfPeriod": 1461967200000,
"rate": 0.03708314
},
{
"endOfPeriod": 1461708000000,
"rate": 0.03492851
},
{
"endOfPeriod": 1461794400000,
"rate": 0.03845068
},
{
"endOfPeriod": 1461621600000,
"rate": 0.03544827
}
];
const res = myList.reduce((prev, current) => {
const date = new Date(current.endOfPeriod);
const month = date.getMonth();
const year = date.getFullYear();
const key = `${year}-${month}`;
if (prev[key] && prev[key].endOfPeriod < current.endOfPeriod) {
prev[key] = current;
} else {
prev[key] = current;
}
return prev;
}, {});
const finalResult = Object.keys(res).map((key) => {
return {
key: res[key].rate
}
});
console.log(finalResult);

Categories