convert all array object(firestore timestamp) to date - javascript

I am using firestore and react-native-gifted-chat, I am trying to get all the chat messages from firestore to the chat. However, gifted chat does not support displaying firebase timestamp. It will show invalid Date. Therefore, I m trying to convert all the date object.
async _getMessage() {
const messColRef = db.collection('Message').doc(this.state.roomName).collection('message').orderBy('createdAt', 'desc').limit(9)
const initialQuery = messColRef
const documentSnapshots = await initialQuery.get()
const documentData = documentSnapshots.docs.map(document => ({
id: document.id, ...document.data()
}));
const lastVisible = documentData[documentData.length - 1]
const finalData = _.forEach(documentData['createdAt'], (item) => {
return item.toDate()
});
console.log(documentData)
}
and it is how my data look like:
{
"_id": "f0feb0b6-c0f9-4735-a93d-4297872a4840",
"createdAt": Timestamp {
"nanoseconds": 382000000,
"seconds": 1568995812,
},
"id": "Uw6PNNsf7aqWrxcgSDSi",
"text": "Hi",
"user": {
"_id": "V8h2iSllhPXSr8sTGP0yHiaYZwx1",
"avatar": "https://firebasestorage.googleapis.com/v0/b/exit-3684f.appspot.com/o/add-
user.png ? alt = media & token=395c8beb - 47a3 - 4ae6 - a0a1 - fe901e7ad42f",
"name": "This is the username",
},
},
{
"_id": "cc298d96-f19a-4ec7-bdf7-3767d900a364",
"createdAt": Timestamp {
"nanoseconds": 373000000,
"seconds": 1568995733,
},
"id": "WzbOA52Y3qukvPUIXRLB",
"text": "hello",
"user": {
"_id": "V8h2iSllhPXSr8sTGP0yHiaYZwx1",
"avatar": "https://firebasestorage.googleapis.com/v0/b/exit-3684f.appspot.com/o/add-
user.png ? alt = media & token=395c8beb - 47a3 - 4ae6 - a0a1 - fe901e7ad42f",
"name": "This is the username",
},
},
so my goal is to convert all the createdAt to js time date

Sorry for not explore deeply, after checking #Spatz comments I figure out how to do it
documentData.forEach(a => {
var date = a.createdAt.toDate()
data.push({
_id: a._id,
createdAt: date,
id: a.id,
text: a.text,
user: a.user
})
})

Use renderTime prop of gifted chat and pass a function which converts the time and return that time in a string.

Related

How to cast string boolean from MySQL to real boolean in Javascript?

I built a php backend for my ReactJS frontend.
My mysql users table has a checkedIn column that is stored as tinyint. I am fetching it like:
<?php
// ...
$getUser = "SELECT * FROM `users` WHERE `uid`=:uid AND `unique_id`=:unique_id";
$getUser_stmt = $conn->prepare($getUser);
$getUser_stmt->bindValue(':uid', $uid,PDO::PARAM_STR);
$getUser_stmt->bindValue(':unique_id', $unique_id,PDO::PARAM_STR);
$getUser_stmt->execute();
//...
And in my frontend:
useEffect(() => {
axios
.get(`${config.server}/api/user.php?uid=${match.params.uid}`)
.then((result) => {
console.log(result.data.message);
setUser(result.data.message);
})
.catch((error) => console.error(error));
}, [match.params.uid]);
My log gives me:
{
"id": "5",
"uid": "A0005",
"unique_id": "1384773b4df62",
"mail": "mail#test.com",
"date_checkin": "2021-05-03 11:00:35",
"checkedIn": "1"
}
How can I modify my backend or my frontend, to have a real boolean value for checkedIn in the frontend?
I expect:
{
"id": 5,
"uid": "A0005",
"unique_id": "1384773b4df62",
"mail": "mail#test.com",
"date_checkin": Date Mon May 03 2021 17:15:27 GMT+0200,
"checkedIn": true
}
As a side effect: How can I cast the id to Number and the timestamp to Date?
Create a function convert do these conversion:
let user = `{
"id": "5",
"uid": "A0005",
"unique_id": "1384773b4df62",
"mail": "mail#test.com",
"date_checkin": "2021-05-03 11:00:35",
"checkedIn": "1"}`;
function convert(data){
data["id"] = data["id"]*1;//Int
data["checkedIn"] = !(data["checkedIn"]==='0');//Bool
data["date_checkin"] = Date(data["date_checkin"]);//Date
return data;
}
convert(JSON.parse(user));
Note: user has JSON format. So convert it to real object using function JSON.parse()

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

Normalize objects in array such that all contain the same set of keys

I am hitting an endpoint that is returning an array of objects, each object can potentially have a set of fields, e.g.,
const FIELDS = [
'id',
'title',
'contributor',
'mediatype',
'source'
]
However, some objects will only have some of those fields, some may have all.
const items = [
{
"id": 1,
"title": "some title 1",
"contributor": "bob",
"mediatype": "text"
},
{
"id": 2,
"title": "some title 2",
"mediatype": "text"
}.
{
"id": 3,
"title": "some title 3",
"mediatype": "movies"
"source": "comcast"
}
]
I want to "normalize" all the objects such that every single one contains every expected field, filling the "gaps" with null, or some falsey value such that graphql (which I intend to eventually feed it into) is happy.
const items = [
{
"id": 1,
"title": "some title 1",
"contributor": "bob",
"mediatype": "text",
"source": null
},
{
"id": 2,
"title": "some title 2",
"mediatype": "text",
"contributor": null,
"source": null
}.
{
"id": 3,
"title": "some title 3",
"mediatype": "movies",
"contributor": null,
"source": "comcast"
}
]
My "nasty" looking code looks something like this
const normalize = items =>
items.map(item => {
FIELDS.forEach(f => {
if (!item[f]) {
item[f] = null;
}
});
return item;
});
Any suggestions for writing this more elegantly - either with vanilla JS or lodash, which I am equally open to using as its already available in my codebase?
You can use spread syntax, but then it would be better to define FIELDS as a template object:
const FIELDS = {
id: null,
title: null,
contributor: null,
mediatype: null,
source: null
};
const normalize = items => items.map(item => ({...FIELDS, ...item}));
Your if (!item[f]) test will match on any falsy value, which is probably not what you want.
Instead, you should properly check if the key exists, e.g.:
if (!(f in item))
Not sure if this is any better really... but here is some equivalent alternative syntax.
Use an "equals itself or null" to squeeze out a bit more sugar:
const normalize = items =>
items.map(item => {
FIELDS.forEach(f => item[f] = item[f] || null);
return item;
});
Or test everyone's patience with this one liner:
const normalize = items =>
items.map(item => FIELDS.reduce((acc, field) => {acc[field] = item[field] || null; return acc}, item));
The choice is yours.

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

Using Jest property matchers on arrays of objects

I am attempting to use Jest's new Property Matcher feature (since Jest 23.0.0) to match on an array of objects that contain a generated field. I have tried putting both a plain object and a matcher definition using expect.arrayContaining and expect.objectContaining like I might when matching manually. Is there any way to do this currently?
const sportsBallPeople = [
{
createdAt: new Date(),
name: 'That one famous guy from Cleveland'
},
{
createdAt: new Date(),
name: 'That tall guy'
}
];
expect(sportsBallPeople).toMatchSnapshot(<something goes here>);
Version Info
As is noted in the question, property matchers were introduced in Jest 23.0.0. Note that apps bootstrapped with create-react-app as of today (Aug 5, 2018) are still < 23.
OBJECT
Here is an example using a property matcher for a single object:
test('sportsBallPerson', () => {
expect(sportsBallPeople[0]).toMatchSnapshot({
createdAt: expect.any(Date)
})
});
The snapshot generated:
exports[`sportsBallPerson 1`] = `
Object {
"createdAt": Any<Date>,
"name": "That one famous guy from Cleveland",
}
`;
This will correctly match createdAt to any date and the name to exactly "That one famous guy from Cleveland".
ARRAY
To test an array of objects using property matchers use forEach to loop over the array and snapshot test each object individually:
test('sportsBallPeople', () => {
sportsBallPeople.forEach((sportsBallPerson) => {
expect(sportsBallPerson).toMatchSnapshot({
createdAt: expect.any(Date)
});
});
});
The snapshots generated:
exports[`sportsBallPeople 1`] = `
Object {
"createdAt": Any<Date>,
"name": "That one famous guy from Cleveland",
}
`;
exports[`sportsBallPeople 2`] = `
Object {
"createdAt": Any<Date>,
"name": "That tall guy",
}
`;
forEach ensures that the objects are tested in order, and each object is properly snapshot tested as described above.
Additional Info
It is interesting to note that directly testing an array using property matchers does not work properly and has unexpected side-effects.
My first attempt to test an array was to create the following test:
test('sportsBallPeople as array', () => {
expect(sportsBallPeople).toMatchSnapshot([
{ createdAt: expect.any(Date) },
{ createdAt: expect.any(Date) }
]);
});
It generated the following snapshot:
exports[`sportsBallPeople as array 1`] = `
Array [
Object {
"createdAt": Any<Date>,
},
Object {
"createdAt": Any<Date>,
},
]
`;
This is incorrect since the name properties are missing, but the test still passes (Jest v23.4.2). The test passes even if the names are changed and additional properties are added.
Even more interesting was that as soon as this test executed, any following tests using property matchers were adversely affected. For example, placing this test ahead of the the test looping over the objects changed those snapshots to the following:
exports[`sportsBallPeople 1`] = `
Object {
"createdAt": Any<Date>,
}
`;
exports[`sportsBallPeople 2`] = `
Object {
"createdAt": Any<Date>,
}
`;
In summary, directly passing an array to use with property matchers does not work and can negatively affect other snapshot tests using property matchers.
toMatchObject works for an array too because arrays are objects
expect(receivedArray).toMatchObject(expectedArray)
To apply snapshot property matcher to each array entry without creating a separate assertion for each, we may create an array with length equal to the value's length filled with the matcher definition:
it('should return first 10 notes by default', async () => {
const noteMatcher = {
createdAt: expect.any(String),
updatedAt: expect.any(String),
}
const response = await app.inject({
method: 'GET',
url: `/examples/users/1/notes`,
})
const payload = response.json()
const { length } = payload.data
expect(response.statusCode).toMatchInlineSnapshot(`200`)
expect(length).toBe(10)
expect(payload).toMatchSnapshot({
data: new Array(length).fill(noteMatcher),
})
})
Will result in the following snapshot:
exports[`should return first 10 notes by default 2`] = `
Object {
"data": Array [
Object {
"createdAt": Any<String>,
"deletedAt": null,
"id": 1,
"note": "Note 1",
"title": "Title 1",
"updatedAt": Any<String>,
"userID": 1,
},
Object {
"createdAt": Any<String>,
"deletedAt": null,
"id": 2,
"note": "Note 2",
"title": "Title 2",
"updatedAt": Any<String>,
"userID": 1,
},
// 8 omitted entries
],
"success": true,
}
`;
Create an array of Property Matcher (using Array(n).fill(matcher) for example), of the same size as the result object you want to match (n=sportsBallPeople.length). matcher representing here the Property Matcher of one item of your array.
That way:
It will check each element of the array with the property matcher.
It will create only one snapshot with the full array.
If the result is not the same size as the last snapshot, the test will fail because the snapshot will be different. So it will fail even if the new result is bigger
(others answers may not fail when the array grow if they create one snapshot per item, as new snapshot are usually silently created in CI and doesn't trigger a test failure)
const sportsBallPeople = [
{
createdAt: new Date(),
name: 'That one famous guy from Cleveland'
},
{
createdAt: new Date(),
name: 'That tall guy'
}
];
const itemMatcher = {
createdAt: expect.any(Date),
}
const arrayMatcher = Array(sportsBallPeople.length).fill(itemMatcher)
expect(sportsBallPeople).toMatchSnapshot(arrayMatcher);
or, simply:
expect(sportsBallPeople).toMatchSnapshot(Array(sportsBallPeople.length).fill({
createdAt: expect.any(Date),
}));
Resulting snapshot will be:
exports[`snapshot 1`] = `
Array [
Object {
"createdAt": Any<Date>,
"name": "That one famous guy from Cleveland",
},
Object {
"createdAt": Any<Date>,
"name": "That tall guy",
},
]`
Thanks for the tips.
Often, being a test you can control the inputs making something like the following viable.
describe.only('Something', () => {
it.only('should do something', () => {
const x = {
a: false,
b: true,
c: 157286400,
};
const results = functionBeingTesting(x, 84);
expect(results[0]).toMatchInlineSnapshot({
createdAt: expect.any(Number),
updatedAt: expect.any(Number)
},
`
Object {
"createdAt": Any<Number>,
"a": false,
"b": true,
"updatedAt": Any<Number>,
"value": "0",
}
`,
);
expect(results[1]).toMatchInlineSnapshot({
createdAt: expect.any(Number),
updatedAt: expect.any(Number)
},
`
Object {
"createdAt": Any<Number>,
"a": false,
"b": true,
"updatedAt": Any<Number>,
"value": "1",
}
`,
);
expect(results[2]).toMatchInlineSnapshot({
createdAt: expect.any(Number),
updatedAt: expect.any(Number)
},
`
Object {
"createdAt": Any<Number>,
"a": false,
"b": true,
"updatedAt": Any<Number>,
"value": "1",
}
`,
);
});
});

Categories