I have this schema
const personSchema = new Schema({
name: {
type: String,
required: true
},
children: {
type: Array,
default: [],
required: false
}
}, { timestamps: true });
Inside children I can have another person object.
ex:
{ name: 'yoshi',
id: 01,
children: [
{ name: 'luigi',
id: 02,
children: []
},
{ name: 'mario',
id: 03,
children: [
{ name: 'bowser',
id: 04
children: []
}
]
If I only have 'bowser' ID (04), how to get 'bowser' object and it's children?
I try Person.findById(04), but I can't get it.
I only can get yoshi (01) using findById
Is there a way to ignore all level and focus only getting by ID?
I dont want to use find(children.children.id), because I have dynamic depth level.
Related
For an exercise I need to validate some data with a schema. I could validate the first level (id, name, value), checked if it's required or not, and that the type asked matched but I couldn't find a way for the nested one parents, do you know how it's possible?
function validate(data, schema){...}
const data = {
cards: [{
id: 1,
name: "Paris",
value: 99,
parents: [{
id: 8,
name: "Parent 1",
value: 200,
parents: []
}]
}]
}
const schema = [{
name: 'id',
required: true,
type: 'number'
}, {
name: 'name',
required: true,
type: 'string'
}, {
name: 'value',
required: true,
type: 'number'
}, {
name: 'parents',
required: false,
type: 'array:card'
}]
I'm stuck for the type array:card, where I'm supposed to check if what's within parents is similar to the root of the object. If possible without doing a for within a for because technically this object can be nested to infinity.
Thanks for your help
Recursion feels like it was born for this kind of problem. This demo uses slightly simplified validation criteria.
// Recursively applies a validation function to each card
const {cards} = getData();
for(const card of cards){
validate(card);
}
// Defines the validation function
function validate(node){
// Does something conditionally based on whether node passes tests
const valid =
typeof node.id == 'number' &&
typeof node.name == 'string' &&
typeof node.value == 'number' &&
Array.isArray(node.parents);
if(valid){ console.log(`node ${node.id} looks good`); }
else{ console.error(`node ${node.id} is invalid`); }
// If possible, moves down a level to validate more nodes
if(node.parents?.length){
for(const parent of node.parents){
validate(parent); // Recursive call
}
}
}
// Gets the initial data
function getData(){
return {
cards: [{
id: 1,
name: "Paris",
value: 99,
parents: [{
id: 8,
name: null,
value: 200,
parents: [{ id: 23, name: "Crockford", value: 86, parents: [] }]
}, {
id: 42,
name: "Nibbler",
value: 123,
parents: []
}]
}]
}
}
I am working in a ecommerce website. I have create a mongoose model for all categories. But including parent id for the category that is a subcategory.
when get a request send, all the categories are finding from database. That's fine but I want to send the category in a nested way to client. That's why I wrote like this.
// This is mongoose model
const mongoose = require('mongoose');
const categorySchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
},
parentId: {
type: mongoose.Schema.Types.ObjectId
}
}, { timestamps: true })
module.exports = mongoose.model("Category", categorySchema)
// This is router
Router.get('/categories', async (req, res) => {
const createCategory = (categories, parentId = null) => {
const categoryList = []
let category
if (parentId === null) {
category = categories.filter(cat => cat.parentId === undefined);
} else {
category = categories.filter(cat => cat.parentId === parentId)
}
category.map((cate) => {
categoryList.push({
_id: cate._id,
name: cate.name,
slug: cate.slug,
children: createCategory(categories, cate._id)
})
})
return categoryList
}
try {
const categories = await Category.find({})
const categoryList = createCategory(categories)
res.status(200).json({ categoryList })
} catch (error) {
res.status(500).json(error)
}
})
// the data of databess
[
{
_id: new ObjectId("613c4ae2ff0098e41b1ae89a"),
name: 'Mobile',
slug: 'Mobile',
createdAt: 2021-09-11T06:21:22.137Z,
updatedAt: 2021-09-11T06:21:22.137Z,
__v: 0
},
{
_id: new ObjectId("613c4b11ff0098e41b1ae89c"),
name: 'Oppo',
slug: 'Oppo',
parentId: '613c4ae2ff0098e41b1ae89a',
createdAt: 2021-09-11T06:22:09.068Z,
updatedAt: 2021-09-11T06:22:09.068Z,
__v: 0
},
{
_id: new ObjectId("613c4b21ff0098e41b1ae89e"),
name: 'Samsung',
slug: 'Samsung',
parentId: '613c4ae2ff0098e41b1ae89a',
createdAt: 2021-09-11T06:22:25.359Z,
updatedAt: 2021-09-11T06:22:25.359Z,
__v: 0
},
{
_id: new ObjectId("613c4b2fff0098e41b1ae8a0"),
name: 'Nokia',
slug: 'Nokia',
parentId: '613c4ae2ff0098e41b1ae89a',
createdAt: 2021-09-11T06:22:39.048Z,
updatedAt: 2021-09-11T06:22:39.048Z,
__v: 0
},
{
_id: new ObjectId("613c4b47ff0098e41b1ae8a2"),
name: 'Nokia 2.4',
slug: 'Nokia-2.4',
parentId: '613c4ae2ff0098e41b1ae89a',
createdAt: 2021-09-11T06:23:03.580Z,
updatedAt: 2021-09-11T06:23:03.580Z,
__v: 0
},
{
_id: new ObjectId("613c4b6cff0098e41b1ae8a4"),
name: 'TV',
slug: 'TV',
createdAt: 2021-09-11T06:23:40.550Z,
updatedAt: 2021-09-11T06:23:40.550Z,
__v: 0
},
{
_id: new ObjectId("613c4b7eff0098e41b1ae8a6"),
name: 'Refrijerator',
slug: 'Refrijerator',
createdAt: 2021-09-11T06:23:58.782Z,
updatedAt: 2021-09-11T06:23:58.782Z,
__v: 0
}
]
Them problem is when I trying to get data from client only root level category is outputting which has no parent id and but children array is empty like this.
{
"categoryList": [
{
"_id": "613c4ae2ff0098e41b1ae89a",
"name": "Mobile",
"slug": "Mobile",
"children": []
},
{
"_id": "613c4b6cff0098e41b1ae8a4",
"name": "TV",
"slug": "TV",
"children": []
},
{
"_id": "613c4b7eff0098e41b1ae8a6",
"name": "Refrijerator",
"slug": "Refrijerator",
"children": []
}
]
}
I think the "createCategory" recuresive function is not working.
please anyone help me I am new in this field...
As the _id properties are instances of ObjectId, they will never match with parentId, which are strings.
According to documentation:
ObjectId() has the following attribute and methods:
Attribute/Method
Description
str
Returns the hexadecimal string representation of the object.
...
...
So change this line:
children: createCategory(categories, cate._id)
to:
children: createCategory(categories, cate._id.str)
Remark
Your use of .map is an antipattern. You should take its return value and not use push in the callback. Replace these two statements:
const categoryList = []
/* .. */
category.map((cate) => {
categoryList.push({
_id: cate._id,
name: cate.name,
slug: cate.slug,
children: createCategory(categories, cate._id.str)
})
})
...with this single statement:
const categoryList = category.map((cate) => ({
_id: cate._id,
name: cate.name,
slug: cate.slug,
children: createCategory(categories, cate._id.str)
}))
Secondly, you can avoid code duplication at the start of your function, by just not providing a default value to the parentId argument. So that function can be become much more compact:
const createCategory = (categories, parentId) => {
const category = categories.filter(cat => cat.parentId === parentId);
return category.map((cate) => ({
_id: cate._id,
name: cate.name,
slug: cate.slug,
children: createCategory(categories, cate._id.str)
}));
};
change the code below
children: createCategory(categories, cate._id)
To:
children: createCategory(categories, cate._id.toString())
I am trying to compare two objects using lodash like below. The problem is that it always returns false. I think that the issue is that the objects have different order of keys and values. I however couldn't find a solution on how to compare it regardless on the order.
How to ignore the order and compare the two objects correctly?
var obj1 = {
event: 'pageInformation',
page: { type: 'event', category: 'sportsbook' },
username: 'anonymous',
pagePath: '/',
item: { name: 'Barcelona - Leganes', id: '123' },
contest: { name: '1.Španielsko', id: 'MSK70' },
category: { name: 'Futbal', id: 'MSK3' },
teams: [
{ id: 'barcelona', name: 'Barcelona' },
{ id: 'leganes', name: 'Leganes' }
]
}
var obj2 = {
event: 'pageInformation',
page: { type: 'event', category: 'sportsbook' },
username: 'anonymous',
pagePath: '/',
category: { id: 'MSK3', name: 'Futbal' },
contest: { name: '1.Španielsko', id: 'MSK70' },
item: { id: '123', name: 'Barcelona - Leganes' },
teams: [
{ name: 'Barcelona', id: 'barcelona' },
{ name: 'Leganes', id: 'leganes' }
]
}
function compareObjects(obj1, obj2){
return _.isMatch(obj1, obj2);
}
You can use the isEqual function which does a deep equal check (regardless of key order):
_.isEqual(obj1, obj2)
See more here: https://lodash.com/docs/2.4.2#isEqual
I have 2 arrays in JavaScript. One of which needs filtering based on a property from the other one.
I have a movies list such as this:
[
{
id: 1,
type: 'movies',
attributes: {
name: 'Batman'
}
},
{
id: 2,
type: 'movies',
attributes: {
name: 'Spiderman'
}
},
...
]
I then have another array which contains movies that a user has watched but they are within a nested object within the array as a relationship following the JSON API spec.
[
{
id: '1',
type: 'moviesWatched',
attributes: {
comment: 'Excellent film, would recommend to anyone who loves a action film!'
},
relationships: {
movie: {
data: {
type: 'movies',
id: 2
}
}
}
}
]
What I need to achieve is I need to cross reference the id from watchList[].relationships.movie.data.id with the first array. Obviously, this list is a lot longer in production but what I am hoping to achieve is to have a full list of movies which the use has watched with the name of the movie based off 2 arrays formatted like this to save me having to store needless data inside of the database.
The outcome of the array would be something along the lines of...
[
{
id: 1,
type: 'movies',
attributes: {
name: 'Batman'
},
meta: {
watched: false
}
},
{
id: 2,
type: 'movies',
attributes: {
name: 'Spiderman'
},
meta: {
watched: true
}
}
]
Here is what I have currently, which is working but I don't know if there is a better way to go about it...
movies.map((movie) => {
const watched = watchedMovies.find((searchable) => {
return searchable.relationships.movie.data.id === searchable.id;
});
movie.meta.watched = watched || false;
return movie;
});
Since you said the list can be long, you can extract the "watched" movie ids to a Set and then set the meta.watched property of a movie based on whether or not its id is in the set:
const movies = [{
id: 1,
type: 'movies',
attributes: {
name: 'Batman'
}
},
{
id: 2,
type: 'movies',
attributes: {
name: 'Spiderman'
}
}
];
const watched = [{
id: '1',
type: 'moviesWatched',
attributes: {
comment: 'Excellent film, would recommend to anyone who loves a action film!'
},
relationships: {
movie: {
data: {
type: 'movies',
id: 2
}
}
}
}];
const watchedSet = new Set(watched.map(m => m.relationships.movie.data.id));
movies.forEach(movie => movie.meta = {watched: watchedSet.has(movie.id)});
console.log(movies);
I am retrieving a sorted array of comments from the server. Each comment has a comment.children attribute that is also a sorted array of other comments. These can be nested n deep. E.g:
const nestedComments = [
{
id: 1,
body: "parent comment",
children: [
{
id: 4,
body: 'child comment',
children: [
{
id: 7,
body: 'grandchild comment #1',
children: [],
},
{
id: 6,
body: 'grandchild comment #2',
children: [],
},
{
id: 5,
body: 'grandchild comment #3',
children: [],
},
]
},
{
id: 3,
body: 'second child comment',
children: []
}
],
},
{
id: 8,
body: 'parent comment #2',
children: []
},
];
Then I use the normalizr library to normalize it like this:
const commentSchema = new schema.Entity('comments');
const commentListSchema = new schema.Array(commentSchema);
commentSchema.define({children: commentListSchema});
const normalizedComments = normalize(nestedComments, commentListSchema);
The result is pretty much as expected:
{
entities: {
// All of the comments ordered their id's, this is fine and what I want
},
results: [1,8] // understandable but not what I want
}
So it preserves the order of the root comments but doesn't do anything for the nested child comments. Is there a way to do this so that each group of siblings has its own results array? Something that looks like this:
results: [[1,8], [4,3], [7,6,5]];
Or maybe there is a better way to preserve that information and I'd appreciate hearing about that as well.
The answer that will work for me was already present in the results. The comments in the entities stores an array of the children ids that is in the original order.
entities: {
{
id: 1,
body: 'parent comment',
children: [4,3]
},
...
{
id: 4,
body: 'child comment',
children: [7,6,5],
},
...
}