console.log not showing expected object properties - javascript

I have the following code in javascript in my node.js application.
However certain objects are not stored in my variable appointment. Even if I set them, when I directly access them it works: console.log(appointment.test);
What have I done wrong in this code?
var appointment = {
subscribed: false,
enoughAssis: false,
studentSlotsOpen: false
};
console.log(appointment);
for (var key in appointmentsDB[i]) {
appointment[key] = appointmentsDB[i][key];
}
appointment.test= "res";
console.log(appointment.test);
console.log(appointment);
And here is the produced output:
{ subscribed: false,
enoughAssis: false,
studentSlotsOpen: false }
res
{ comment: 'fsadsf',
room: 'dqfa',
reqAssi: 3,
maxStud: 20,
timeSlot: 8,
week: 31,
year: 2013,
day: 3,
_id: 51f957e1200cb0803f000001,
students: [],
assis: [] }
The variable console.log(appointmentsDB[i]) looks as:
{ comment: 'fsadsf',
room: 'dqfa',
reqAssi: 3,
maxStud: 20,
timeSlot: 8,
week: 31,
year: 2013,
day: 3,
_id: 51f957e1200cb0803f000001,
students: [],
assis: [] }
The following command:
console.log(Object.getOwnPropertyNames(appointmentsDB[i]), Object.getOwnPropertyNames(Object.getPrototypeOf(appointmentsDB[i])));
Shows:
[ '_activePaths',
'_events',
'errors',
'_maxListeners',
'_selected',
'_saveError',
'_posts',
'save',
'_pres',
'_validationError',
'_strictMode',
'isNew',
'_doc',
'_shardval' ] [ 'assis',
'timeSlot',
'db',
'_schema',
'id',
'base',
'day',
'collection',
'reqAssi',
'constructor',
'comment',
'year',
'room',
'students',
'week',
'_id',
'maxStud' ]
However I would expect that my last output also provides the entries test, subscribed, enoughAssis and studentSlotsOpen. What is wrong in this code?
The solution I found was to manually copy the elements I wanted to.

You probably have a Document object instead of a plain object. Those have a custom toJSON method which only yields the properties of your schema and the _id, but nothing else. If you are copying that method with your for-in-loop onto the appointment object, it will get serialized differently as well when logged.
Try
for (var key in appointmentsDB[i].toObject()) {
appointment[key] = appointmentsDB[i][key];
}
appointment.test= "res";
console.log(appointment);

Related

Unable to find item in this array when I do it outside of the map function it works inside it doens't

I am trying to make a calendar with events that are rendered dynamically.
I use this to create the days for the calendar and create an array to display. I try to create an events object containing the details for the events. When I try to outside of the initialDays map fuction it works fine and is able to find the object with the correct date and time however when I try to use find function inside of the initialDays map function I get this error.
events.find((item) => {
return item.datetime == dates
})
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'length')
createDays()
{
this.today = startOfToday()
this.formatedDate = format(this.today, 'MMM, yyyy')
this.currentMonth = this.formatedDate
this.getNewDays = parse(this.currentMonth, 'MMM, yyyy', new Date())
this.firstDayCurrentMonth = parse(this.currentMonth, 'MMM, yyyy', new Date())
let initialDays = eachDayOfInterval({
start: startOfWeek(this.getNewDays, {weekStartsOn: 1}),
end: endOfWeek(endOfMonth(this.getNewDays), {weekStartsOn: 1})
})
let events = [{ id: 3, name: 'Date night', time: '6PM', datetime: '2022-10-10', href: '#' }, { id: 4, name: 'Date night', time: '6PM', datetime: '2022-10-12', href: '#' }, { id: 3, name: 'Date night', time: '6PM', datetime: '2022-10-13', href: '#' }]
events = events.map((events) => {
return {
id: events.id,
name: events.name,
time: events.time,
datetime: events.datetime,
href: events.href
}
})
const findevent = events.find((item) => {
return item.datetime === '2022-10-10'
})
console.log(findevent)
initialDays = initialDays.map((dates) => {
return {
date: format(dates, 'yyyy-MM-dd'),
isCurrentMonth: isSameMonth(this.getNewDays, dates),
isToday: isToday(dates),
isSelected: isSameDay(dates, this.today),
events: events.find((item) => {
return item.datetime == dates
})
}
})
this.days = initialDays
},
EDIT ***
InitialDays returns a array of dates from start of the current month to end of current month plus 1 week before and 1 week after.
I want the events to be filled in if they match dates. However currently it just fills every date with the all the events. I thought using the find method would work. Date-fns has a ```isSameDay`` function however it only returns true or false. Not sure how to continue...
{
"date": "2022-10-22",
"isCurrentMonth": true,
"isToday": false,
"isSelected": false,
"events": [
{
"id": 3,
"name": "Date night",
"time": "6PM",
"datetime": "2022-10-10",
"href": "#"
},
{
"id": 4,
"name": "Date night",
"time": "6PM",
"datetime": "2022-10-12",
"href": "#"
},
{
"id": 3,
"name": "Date night",
"time": "6PM",
"datetime": "2022-10-13",
"href": "#"
}
]
}
First, let's see the events you are working with:
let events = [{ id: 3, name: 'Date night', time: '6PM', datetime: '2022-10-10', href: '#' }, { id: 4, name: 'Date night', time: '6PM', datetime: '2022-10-12', href: '#' }, { id: 3, name: 'Date night', time: '6PM', datetime: '2022-10-13', href: '#' }]
The items are objects and
const findevent = events.find((item) => {
return item.datetime === '2022-10-10'
})
is working for you, which proves that this is operational.
In the snippet below we can see that map does not work when you want to call it for an object
let initialDays = {
start: new Date(),
end: new Date()
};
initialDays = initialDays.map((dates) => {
return dates.length
})
therefore eachDayOfInterval returns an array (otherwise you would get an error, complaining that map is not a function). So, your function of eachDayOfInterval does something that you did not share, so it's unclear what this array will contain.
However, it is very safe to assume that the eachDayOfInterval returns an array of days (in whatever format). When you do something like
initialDays.map((dates) => {
//...
}
you therefore will iterate your dates and each iteration is a date (day). So the naming of dates above shows that you misunderstood this part and you assumed that you will have an array of dates inside the map callback, but you will have an individual date inside that callback instead, looping through the whole array.
The error suggests that you tried to read the property of length on undefined. This proves the following:
your dates has at least one undefined element, which is the technical reason you get this error
you treat the individual date as if it was an array of dates
items.map(item => something(item))
will loop items and perform a mapping for each item.

Dynamically split an array into subarrays

const giftcards = [
{
fromuserid: 1,
amount: 10,
date: new Date(),
touserid: 11,
},
{
fromuserid: 1,
amount: 20,
date: new Date(),
touserid: 11,
},
{
fromuserid: 2,
amount: 10,
date: new Date(),
touserid: 11,
},
{
fromuserid: 2,
amount: 10,
date: new Date(),
touserid: 11,
},
{
fromuserid: 3,
amount: 10,
date: new Date(),
touserid: 11,
}
]
I achieved this, which is shown in the useEffect hook:
const giftcards = [
{
fromuserid: 1,
amounts: [{
amount: 10,
date: new Date(),
touserid: 11,
},
{
amount: 20,
date: new Date(),
touserid: 11,
}
]
},
{
fromuserid: 2,
amounts: [{
amount: 10,
date: new Date(),
touserid: 11,
},
{
amount: 10,
date: new Date(),
touserid: 11,
}
]
},
{
fromuserid: 3,
amounts: [{
amount: 10,
date: new Date(),
touserid: 11,
}]
}
]
The solution that was given works except that i would like to make it dynamic.
Meaning, in my app, I allow the user to arrange how the array will be sorted.
For example,
const [sort, setSort] = useState('fromuserid')
const [results, setResults] = useState([])
<div>
<select value={sort} onChange={(e)=> setSort(e.target.value)}>
<option value='fromuserid'>Sort by Buyer</option>
<option value='touserid'>Sort by Receiver</option>
<option value='date'>Sort by Date</option>
</select>
</div>
then in the:
useEffect(() => {
allgiftcards.forEach(({
fromuserid,
date,
from,
giftcardid,
message,
template,
touserid,
amount,
paid
}) => {
map.has(fromuserid) || map.set(fromuserid, {
fromuserid,
cards: []
})
map.get(fromuserid).cards.push({
date,
from,
giftcardid,
message,
template,
touserid,
amount,
paid
})
})
setResults([...map.values()])
}, [sort])
Here is what i mean by dynamic. If the user selected date, I would like for it to look something like:
useEffect(() => {
allgiftcards.forEach(({
fromuserid,
date,
from,
giftcardid,
message,
template,
touserid,
amount,
paid
}) => {
map.has(date) || map.set(date, {
date,
cards: []
})
map.get(date).cards.push({
date,
from,
giftcardid,
message,
template,
touserid,
amount,
paid
})
})
setResults([...map.values()])
}, [sort])
But It seems to me that having a bunch of if and else statements would be bad coding and would create a lot of extra code, so looking for a nice and clean solution
This really isn't a question of React (or where-ever useEffect comes from). This is really a general Javascript question, and it's a problem that suits itself well for solving with functional programming, or at least, with one of the staples of functional programming: reduce
In this case, you could supply the 2nd argument which is the initial value of the accumulator -- in this example an empty object works well. You can choose any key from the data to bucket the results:
// Choose a key that each object in the set has, e.g. 'fromuserid' or 'touserid'
const group_by = 'fromuserid';
let bucketed = giftcards.reduce(function (acc, x) {
let pivot = x[group_by]
let current_vals = (acc.hasOwnProperty(pivot) ? acc[pivot] : []);
current_vals.push(x);
acc[pivot] = current_vals;
return acc
}, {});
console.log(bucketed);
If you really needed to land on the second data structure you shared, you could jostle your initialValue and the exact placement of values into the accumulator, but hopefully this demonstrate the concept of how to dynamically choose how to group the data.
This codepen shows how to dynamically bucket an array of objects based on a specific property. Below is a sample of the callback function you can pass to the reducer -- included in the example.
function groupBy(accum, current, groupKey){
const val = current[groupKey]
const {[groupKey]:_, ...content} = current
if (val in accum){
accum[val].push(content)
}
else accum[val] = [content]
return accum
}

How to iterate thru multiple values in a single key in mongoose

I have a web app which uses MongoDB as database and I'm trying to iterate thru multiple values inside a single property named passport.
This is my schema:
var EmployeeDBSchema = new Schema({
/* Passport tab schema */
passportInfo: {
passportDetails: []
},
And here's how it looks in Robomongo:
I tried checking if this can be retrieved as an array, so I did below:
console.log(_.map(results, _.property('passportInfo')));
passportArr = _.map(results, _.property('passportInfo'));
console.log("is passport array? " + _.isArray(passportArr));
Result:
Now since it was positive, I tried iterating thru it like a normal array using the ff. code:
_.forEach(passportArr, function (value, key) {
_.forEach(passportArr[key], function(value2, key2){
console.log(key2 + " >> " + value2);
});
});
However, what I got was this:
How can I get the values of passportExpiry, passportNumber and countryOfOrigin?
I'm really having a hard time over this. Hoping somebody can help.
Thank you.
EDIT: Not sure if this will help but, I got the idea for the structure from this Plunker. Main idea behind Passport was the user can add an unlimited number of passport information (hence the passportInfo array). I'm trying to retrieve the data here so I can render it as a CSV file.
UPDATE:
Here's the expanded results as requested (from console.log):
full results
[ { _id: dummyiddontmind123,
employeeID: '123asd12',
desiredRoleOther: 'Other role',
desiredRole3: 'Role 3',
desiredRole2: 'Role 2',
desiredRole1: 'The Role',
isOpenToIntlAssignment: 'Y',
employeeName: 'Jane Doe',
yrsInIT: 1,
visaInfo:
[ { visaCountryOfOrigin: [Object],
visaNumber: 'asd',
visaEntry: 'Single',
visaExpiry: '2017-03-16T16:00:00.000Z',
visaStatus: 'expired' } ],
passportInfo:
[ { countryOfOrigin: [Object],
passportNumber: [Object],
passportExpiry: '2017-03-03' },
{ countryOfOrigin: [Object],
passportNumber: [Object],
passportExpiry: '2017-03-08T16:00:00.000Z' },
{ countryOfOrigin: [Object],
passportNumber: [Object],
passportExpiry: '2017-03-10T16:00:00.000Z' } ] } ]
[ [ { passportExpiry: '2017-03-03',
passportNumber: { '0': 'EB1234567' },
countryOfOrigin: { '0': 'Philippines' } },
{ passportExpiry: '2017-03-08T16:00:00.000Z',
passportNumber: { '1': 'AS1234' },
countryOfOrigin: { '1': 'Japan' } },
{ passportExpiry: '2017-03-10T16:00:00.000Z',
passportNumber: { '2': 'AX123' },
countryOfOrigin: { '2': 'Singapore' } } ] ]
Your data inside passportInfo is a bit off, probably due to some copy-paste error after outputting it.
I take it you want to export all stored passport information into a csv of format country; number; expiry.
The first thing you want to make sure is that the actual data and the data you expect are structurally the same. If not, you can still add transformation steps before (e.g. flatten arrays or transform objects from {0: 123} to [123]).
As soon as this is under control, you can start by mapping the objects of employee.passportInfo from a structured object to an array of information necessary for your csv. This happens using Array.prototype.map.
I added another step inside that map to make sure an object of passportInfo.passportNumber of the form {0: 123} is transformed into an array [123]. This array is then used to map to a single line of your csv by adding passportInfo.countryOfOrigin and .passportExpiry.
// The following code snippets only operates on one employee. If you have an array use an iteration function depending on your needs.
const employee = { _id: 123,
employeeID: '123asd12',
desiredRoleOther: 'Other role',
desiredRole3: 'Role 3',
desiredRole2: 'Role 2',
desiredRole1: 'The Role',
isOpenToIntlAssignment: 'Y',
employeeName: 'Jane Doe',
yrsInIT: 1,
visaInfo: [ {
visaCountryOfOrigin: [Object],
visaNumber: 'asd',
visaEntry: 'Single',
visaExpiry: '2017-03-16T16:00:00.000Z',
visaStatus: 'expired' }
],
passportInfo: [ {
countryOfOrigin: 'ABC',
passportNumber: { 0: '123123123' },
passportExpiry: '2017-03-03'
}, {
countryOfOrigin: 'DEF',
passportNumber: { 0: '321321321', 1: '123123123' },
passportExpiry: '2017-03-08T16:00:00.000Z'
}, {
countryOfOrigin: 'GHI',
passportNumber: { 0: '654654654' },
passportExpiry: '2017-03-10T16:00:00.000Z'
} ]
};
const flattenPassportNumbers = numbers =>
Object.keys(numbers).map(key => numbers[key]);
const info = employee.passportInfo.map(({passportNumber, passportExpiry, countryOfOrigin}) =>
flattenPassportNumbers(passportNumber).map(number =>
[countryOfOrigin, number, passportExpiry]
)
);
const flattenLine = ([line]) => line;
const joinLine = (line) => line.join('; ');
const lines = info.map(flattenLine);
console.log(lines.map(joinLine));
console.log(lines.map(joinLine).join('\n'));
If there's something you don't understand, please don't hesitate to ask.

Unit test with moment.js

I'm new to writing unit tests.
My function looks like this:
getData() {
return this.parameters.map(p => {
return {
name: p.name,
items: p.items.map(item => {
const toTime = item.hasOwnProperty('end') ? moment.utc(item.end._d).unix() : null;
const fromTime = item.hasOwnProperty('start') ? moment.utc(item.start._d).unix() : null;
return {
id: item.id,
fromTime: fromTime,
toTime: toTime,
};
}),
};
});
}
and so far my Jasmine test looks like this:
describe('getData()', function() {
it('should return json data', function() {
$ctrl.parameters = [{
name: 'test',
items: [{
id: 1,
fromTime: null,
toTime: null
}, {
id: 13,
fromTime: null,
toTime: null
}]
}];
expect($ctrl.getData()).toEqual([{
name: 'test',
items: [{
id: 1,
fromTime: null,
toTime: null
}, {
id: 13,
fromTime: null,
toTime: null
}]
}]);
});
});
This test is working/passing, but as you can see I am not testing the ternary if/else that uses Moment.js. Basically what the ternary does is check if items contains a property called start / end and if it does, convert that value to a epoch/Unix timestamp and assign it to either toTime or fromTime.
So if items had a property called end with a value of 'Sat Oct 31 2015 00:00:00 GMT+0000 (GMT)', then it would be converted to '1446249600' and assigned to toTime.
How can I write a test for it?
The simplest option is to just construct a couple example dates manually for the input. For example:
$ctrl.parameters = [{
name: 'test',
items: [{
id: 1,
start: moment.utc('2017-01-01T01:00:00'),
end: moment.utc('2017-01-01T06:00:00')
}, {
id: 13,
start: moment.utc('2017-01-02T08:00:00'),
end: null
}]
}];
(Note in the above example, I changed fromTime and toTime to start and end, respectively, since that is what getData is expecting for the input.)
Then figure out their unix timestamps. You can do this part externally - for example, I just opened up the browser developer tools (F12) on the moment.js website, evaluated the following statements in the console, and grabbed the timestamp values:
moment.utc('2017-01-01T01:00:00').unix()
moment.utc('2017-01-01T06:00:00').unix()
moment.utc('2017-01-02T08:00:00').unix()
Finally, back in the unit test, just verify that the timestamps match the expected values:
expect($ctrl.getData()).toEqual([{
name: 'test',
items: [{
id: 1,
fromTime: 1483232400,
toTime: 1483250400
}, {
id: 13,
fromTime: 1483344000,
toTime: null
}]
}]);
Alternatively, if you would rather not have hardcoded timestamps in your unit tests, you can instead store each example date in its own variable (e.g., start1, end1), and then compare to, e.g., start1.unix():
// Arrange
const start1 = moment.utc('2017-01-01T01:00:00');
const end1 = moment.utc('2017-01-01T06:00:00');
const start2 = moment.utc('2017-01-02T08:00:00');
$ctrl.parameters = [{
name: 'test',
items: [{
id: 1,
start: start1,
end: end1
}, {
id: 13,
start: start2,
end: null
}]
}];
// Act
const result = $ctrl.getData();
// Assert
expect(result).toEqual([{
name: 'test',
items: [{
id: 1,
fromTime: start1.unix(),
toTime: end1.unix()
}, {
id: 13,
fromTime: start2.unix(),
toTime: null
}]
}]);
That's perfectly fine, since the unit test is meant to test your code, not moment.js. It's up to you.
Note also that I am using the Arrange-Act-Assert pattern for organizing the test. Again, up to you, but once your unit tests start to get complicated, that tends to keep things easier to follow.
Either way, you will need to change how you compute toTime and fromTime in your getData method, since the code as written will not work if you pass in null for either start or end. In particular, item.hasOwnProperty('start') will return true if you pass in a null value for start, but it will error out because it tries to evaluate item.start._d.
Instead, I recommend changing those 2 lines to the following:
const toTime = item.end ? moment.utc(item.end._d).unix() : null;
const fromTime = item.start ? moment.utc(item.start._d).unix() : null;
I would also advise against using the _d property of the moment object, since that is an internal (private) variable. Since start and end are already moment objects, you may instead be able to just do this:
const toTime = item.end ? item.end.unix() : null;
const fromTime = item.start ? item.start.unix() : null;
A complete jsbin example containing all of the above recommended changes is available here:
https://jsbin.com/xuruwuzive/edit?js,output
Note that some tweaks had to be made so it runs outside the context of AngularJS (make getData a standalone function that accepts its parameters directly, rather than via $ctrl).

Insert array of objects into MongoDB

I wonder how I could insert array of objects to Mongo collection "root-level documents" with own pre-defined _id values.
I have tried db.MyCollection.insert(array); but it creates nested documents under one single generated _id in MongoDB.
var array = [
{ _id: 'rg8nsoqsxhpNYho2N',
goals: 0,
assists: 1,
total: 1 },
{ _id: 'yKMx6sHQboL5m8Lqx',
goals: 0,
assists: 1,
total: 1 }];
db.MyCollection.insert(array);
What I want
db.collection.insertMany() is what you need (supported from 3.2):
db.users.insertMany(
[
{ name: "bob", age: 42, status: "A", },
{ name: "ahn", age: 22, status: "A", },
{ name: "xi", age: 34, status: "D", }
]
)
output:
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("57d6c1d02e9af409e0553dff"),
ObjectId("57d6c1d02323d119e0b3c0e8"),
ObjectId("57d6c1d22323d119e0b3c16c")
]
}
Why not iterate over the array objects, and insert them one at a time?
array.forEach((item) => db.MyCollection.insert(item));
Go through this Link To get Exact Outcome the way you want:
https://docs.mongodb.org/manual/tutorial/insert-documents/#insert-a-document
You can use MongoDB Bulk to insert multiple document in one single call to the database.
First iterate over your array and call the bulk method for each item:
bulk.insert(item)
After the loop, call execute:
bulk.execute()
Take a look at the refereed documentation to learn more.

Categories

Resources