I want to get objects with populate and field of the array that I want to count.
For Example:
const ChildSchema = new mongoose.Schema({
name: String,
age: Number,
siblings: [{type: ObjectId, ref: 'Child'}],
toys: [{type: ObjectId, ref: 'Toy'}]
})
and I want to get a populate of the siblings and count of the toys in one object, like this:
const Dan = {
name: "Dan",
age: 4,
siblings: [
{
name: "Sally",
age: 7
},
{
name: "Ben",
age: 10
},
{
name: "Emily",
age: 2
}
],
numOfToys: 11
}
I have this already:
const returnedChild = await ChildModel.findById(BenId)
.populate('siblings', 'name age')
.select('name age siblings')
.lean()
How do I include the count of the toy in the returned object?
Just use virtual schemas:
ChildSchema.virtual('toyCount').get(function () {
return this.toys.length
});
const returnedChild = await ChildModel.findById(BenId)
.populate('siblings', 'name age')
.select('name age siblings')
.lean()
console.log("Toys -> ", returnedChild.toyCount)
Related
I am trying to create a .forEach() in which for every user object in users, certain properties of user are mapped then pushed to players array as new objects apart from eachother.
However in my attempt, all the results of user stack into one object. How can I make sure each .push() creates a new object for each user
What I Want
[
{
name: 'Johnny',
id: '123456789',
lives: 3
},
{
name: 'Timmy',
id: '987654321',
lives: 3
},
]
What I Get
[
{
name: [ 'Johnny', 'Timmy' ],
id: [ '123456789', '987654321' ],
lives: 3
}
]
Code
let players = []
const users = {...}
users.forEach(user => {
let username = user.map(user => user.username)
let id = user.map(user => user.id)
players.push({ name: username, id: id, lives: 3 })
})
console.log(players)
You can accomplish this with a simple map call. I think you want something more like:
const users = [
{ username: 'alice', id: 123 },
{ username: 'bob', id: 456 },
]
const players = users.map(user => ({
name: user.username,
id: user.id,
lives: 3
}))
console.log(players);
Say I have 2 arrays users and userCity. I want to map through users array and return updated user object with merged city data from userCity array based on related userId
I get the error:
> *TypeError: Cannot read property 'city' of undefined
> at user.map.u (eval at <anonymous> (:7:47),*
const users = [
{ userId: 1, name: "Jim", age: 25 },
{ userId: 2, name: "Rens", age: 15 },
{ userId: 3, name: "Ed", age: 5 }
];
const userCity = [{ userId: 1, city: "TX" }, { userId: 3, city: "NY", age: 5 }];
const info = users.map(u => {
return {
...u,
city: userCity.find(uc => {
return uc.userId === u.userId;
}).city
};
});
console.log(info);
Note:
I read somewhere that higher-order functions are synchronous therefore I expect the map function to return the values and assign them to info variable.
So I expect the console.log output to be an array with merged user and city info based on userId
[{ userId: 1, name: "Jim", age: 25, city: "TX" },
{ userId: 3, name: "Ed", age: 5, city: "NY" }]
Instead of iterating through users, you will want to iterate through userCity, since you only want to merge data into that array of objects (not the other way round).
With that in mind, when you iterate through userCity, you simply fetch the matching user from the users array by using Array.prototype.find(), using a predicate/callback that enforce a userID match:
const users = [
{ userId: 1, name: "Jim", age: 25 },
{ userId: 2, name: "Rens", age: 15 },
{ userId: 3, name: "Ed", age: 5 }
];
const userCity = [{ userId: 1, city: "TX" }, { userId: 3, city: "NY", age: 5 }];
const info = userCity.map(c => {
const user = users.find(u => u.userId = c.userId);
return {...c, ...user};
});
console.log(info);
You need to guard yourself against city info not being found.
const users = [{
userId: 1,
name: "Jim",
age: 25
},
{
userId: 2,
name: "Rens",
age: 15
},
{
userId: 3,
name: "Ed",
age: 5
}
];
const userCity = [{
userId: 1,
city: "TX"
}, {
userId: 3,
city: "NY",
age: 5
}];
const info = users.map(u => {
const foundObj = userCity.find(uc => uc.userId === u.userId);
return foundObj ? {
...u,
city: foundObj.city
} : u;
});
console.log(info);
If rxjs and observables are an option, to provide some modern-ish stream style, you could go this way:
const users = [
{ userId: 1, name: 'Jim', age: 25 },
{ userId: 2, name: 'Rens', age: 15 },
{ userId: 3, name: 'Ed', age: 5 }
];
const userCity = [
{ userId: 1, city: 'TX' },
{ userId: 3, city: 'NY', age: 5 }
];
const users$ = from ( users );
const user_city$ = from ( userCity );
users$.pipe (
switchMap ( x => user_city$.pipe (
filter ( y => x.userId === y.userId ),
map ( y => ( { ...x, ...y } ) )
) ),
scan ( ( acc, curr ) => acc.concat ( curr ), [] ),
last ()
).subscribe ( x => console.log ( x ) );
This should trace out a single array with two merged objects. This sort of pattern is pretty de rigueur (and very useful) these days.
Note you're iterating through users as the top level, though I don't think it'd matter either way.
I have some code where I am looping through some records in a table, and whilst doing this I'm organising this data into a JavaScript object array.
For example the data below.
I need to ensure that if a record "Name" has already been recorded, then rather than create a new item it uses the existing item so that the end result will be an object or array like this:
{
"bobjones": {
"sessions": 7
},
"annfrank": {
"sessions": 4
},
"fredsmith": {
"sessions": 4
}
}
Doing this in PHP is easy, but I'm struggling with this in JS due to the way keys are interpreted. Could somebody provide some direction?
const inputData = [
{ name: 'Bob Jones', date: '----', session: 1 },
{ name: 'Ann Frank', date: '----', session: 1 },
{ name: 'Bob Jones', date: '----', session: 1 },
{ name: 'Fred Smith', date: '----', session: 1 },
]
var outputData = {}
for(let doc of inputData) {
if(outputData[doc.name]) {
outputData[doc.name].session = outputData[doc.name].session + doc.session
} else {
outputData[doc.name] = doc
}
}
console.log(outputData)
Now outputData is:
{ 'Bob Jones': { name: 'Bob Jones', date: '----', session: 2 },
'Ann Frank': { name: 'Ann Frank', date: '----', session: 1 },
'Fred Smith': { name: 'Fred Smith', date: '----', session: 1 } }
You can just test to see if the object property exists. If so, add to its sessions property, if not, set it.
if(obj[prop]) {
obj[prop].sessions += sessions;
} else {
obj[prop] = { sessions };
}
In the first if statement, if you're concerned that obj[prop] may exist but obj[prop].sessions may not, you can change it to if(obj[prop] && obj[prop].sessions) { ... }
You could use the reduce method, assuming that you input data is an array of objects (since, that is generally how you get data when consulting a database).
const table = [
{name: "Bob Jones", date: "12-12-2019", sessions: 3},
{name: "Ann Frank", date: "12-9-2019", sessions: 4},
{name: "Bob Jones", date: "12-9-2019", sessions: 2},
{name: "Fred Smith", date: "12-9-2019", sessions: 4}
];
let res = table.reduce((res, {name, date, sessions}) =>
{
if (res[[name]])
res[[name]].sessions += sessions;
else
res[[name]] = {sessions: sessions};
return res;
}, {});
console.log(res);
I have a kids object that looks like the following:
const kids = {
name: 'john',
extra: {
city: 'London',
hobbies: [
{
id: 'football',
team: 'ABC',
},
{
id: 'basketball',
team: 'DEF',
},
],
},
};
and i have the following object that contains all sports and extra info for each.
const sports = [
{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
},
];
I want to get the list of all ids in the hobbies array and go through each of the sports items in the sports array, and found, add an extra field to that object available and give a value of true, so the result will look like:
const result = [
{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
available: true
},
];
by the way, here is my attempt:
const result = kids.extra.hobbies.map(a => a.id);
for (var key in sports) {
console.log(sports[key].name);
const foundIndex = result.indexOf(sports[key].name);
if ( foundIndex > -1) {
sports[key].available = true;
}
}
console.log(sports)
but this is too long... i am looking one liner looking code and robust logic.
This can be done many ways; however, an easy was is to divide the problem into two steps:
We can first flatten the kid's hobbies into an array by using the Array.map() function:
const hobbies = kids.extra.hobbies.map(hobby => hobby.id);
Then, we can iterate through the sports array and add an active property to any object which is present in the new hobbies array:
const result = sports.map(sport => {
if (hobbies.indexOf(sport.name) !== -1) {
sport.available = true;
}
return sport;
})
Complete Solution
const kids = {
name: 'john',
extra: {
city: 'London',
hobbies: [{
id: 'football',
team: 'ABC',
},
{
id: 'basketball',
team: 'DEF',
},
],
},
};
const sports = [{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
},
];
const hobbies = kids.extra.hobbies.map(hobby => hobby.id);
const result = sports.map(sport => {
if (hobbies.indexOf(sport.name) !== -1) {
sport.available = true;
}
return sport;
})
console.log(result);
Firstly, I would change my data structures to objects. Any time you have a list of things with unique ids, objects will make your life much easier than arrays. With that in mind, if you must use arrays, you could do the following:
const hobbies = kids.extra.hobbies
sports.forEach(s => s.available = hobbies.some(h => h.id === s.name))
Note that this mutates the original sports object (change to map for new), and also adds false/true instead of just true.
Build an array of the found sports first, then map while checking to see if the sports object's name is in it:
const kids = {name:'john',extra:{city:'London',hobbies:[{id:'football',team:'ABC',},{id:'basketball',team:'DEF',},],},}
const sports = [{name:'volleyball',coach:'tom',},{name:'waterpolo',coach:'jack',},{name:'swimming',coach:'kate',},{name:'football',coach:'sara',},];
const sportsInHobbies = kids.extra.hobbies.map(({ id }) => id);
const result = sports.map((sportObj) => {
const available = sportsInHobbies.includes(sportObj.name);
return available ? {...sportObj, available } : { ...sportObj };
});
console.log(result);
An example will speak for itself :
Array of Object :
[{
userId: 'ab4e3870-287e-11e7-b5a1-abb6183e9866',
email: 'email1#hotmail.com'
},{
userId: 'ae149220-2883-11e7-bbf9-1fb134f2b4ad',
email: 'email2#hotmail.com'
}]
Object
{
'ab4e3870-287e-11e7-b5a1-abb6183e9866': { name: 'john', roles: 'detective'},
'ae149220-2883-11e7-bbf9-1fb134f2b4ad': { name: 'james', roles: 'plumber'},
}
The result i'd like would be :
[{
userId: 'ab4e3870-287e-11e7-b5a1-abb6183e9866',
email: 'email1#hotmail.com',
name: 'john',
roles: 'detective'
},{
userId: 'ae149220-2883-11e7-bbf9-1fb134f2b4ad',
email: 'email2#hotmail.com',
name: 'james',
roles: 'plumber'
}]
So basically, the value of the key that match the userId in Object is added to that object in the array.
Is there some simple way I don't see to do that? Without external libraries in ES6?
var data = [{
userId: 'ab4e3870-287e-11e7-b5a1-abb6183e9866',
email: 'email1#hotmail.com'
},{
userId: 'ae149220-2883-11e7-bbf9-1fb134f2b4ad',
email: 'email2#hotmail.com'
}]
var d = {
'ab4e3870-287e-11e7-b5a1-abb6183e9866': { name: 'john', roles: 'detective'},
'ae149220-2883-11e7-bbf9-1fb134f2b4ad': { name: 'james', roles: 'plumber'},
};
Using ES6 spread operator ...
data = data.map(function(item) {
return {...item, ...d[item.userId]}
});
ES5: By adding properties manually
data = data.map(function(item) {
item.name = d[item.userId].name;
item.roles = d[item.userId].roles;
return item;
});