Node Js how to fetch data from database in an hierarchical way - javascript

I'm writing a back code using NodeJs to fetch some data from backend, I want dataBase data to be like this
like this:
data = [{
name: "Admin",
id: '1',
children: [
{ name: "Admin", id: "1" },
{ name: "groupe1", id: "2" },
{
name: "groupe2", id: "1455", children: [
{ name: "groupe2", id: "1455" },
{ name: "gro", id: "5444" },
{ name: "hhrr", id: "45" }
]
}
]
}]
the idea is simple we have a list of group each group has a parent I want to display all the groups list in an hierarchical way the top one of the tree is done
Some groups are parents and groups in the same time and some others are only groups if the group is not parent we add an object with its name and ID in the array of children of his parent
if this groups is a parent that's mean it has children we add an object with its ID and name in the array of children of his parents, and we add property children for the object which is array named children with for the first time an object with the name and the id of the group etc...
i tryed to do this but it did not work
const getParentsByType = async ({ name, _id }) => {
let parentResult = [
{
id: _id,
name: name,
children: [
{
id: _id,
name: name,
},
],
},
];
parentResult= await findParent(_id, parentResult[0].children, 0);
return parentResult;
};
const findParent = async (parentId, parentResult, itemPos) => {
let children = await Models.GroupModel.find({ parent: parentId, status: true }).select('name _id');
for (let i = 0; i < children.length; i++) {
let childrenList = await Models.GroupModel.find({ parent: children[i]._id, status: true }).select('name _id');
if (childrenList.length != 0) {
parentResult.push(buildParentWithChild(children[i]._id, children[i].name));
findParent(children[i]._id,parentResult.children[i],itemPos++)
} else {
parentResult.push(buildParent(children[i]._id, children[i].name));
}
}
return parentResult
};
and this the model of the data base
const Group = mongoose.Schema({
name: {
type: String,
required: true,
},
status: {
type: Boolean,
required: true,
},
parent: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Group',
},
});
i had two days trying to resolve tis but with no result
i need some helps and Thank you

Try parsing your returned data. It validates your data as objects i dont see any problem with your function regardless i still have no idea what format your a trying to build.
let children = JSON.parse(JSON.stringify(await Models.GroupModel.find({ parent: parentId, status: true }).select('name _id')));
let childrenList = JSON.parse(JSON.stringify(await Models.GroupModel.find({ parent: children[i]._id, status: true }).select('name _id')));

If I understand you right, you want to convert the array returned by Models.GroupModel.find, and which looks like
var dbresult = [
{_id: "1", parent: null, name: "one"},
{_id: "2", parent: "1", name: "two"}
];
into a hierarchical structure. This can be done with a function that adds all children of a given parent p, including, recursively, their children. Like the following:
function children(p) {
var result = [];
for (r of dbresult) if (r.parent === p) {
var row = {_id: r._id, name: r.name};
var chld = children(r._id);
if (chld.length > 0) row.children = chld;
result.push(row);
}
return result;
}
console.log(JSON.stringify(children(null)));
Note that this approach requires only one database access (to fill the dbresult) and is therefore probably faster than your findParent function.

Related

Javascript: How to create nested from a linear array based on parent ID [duplicate]

This question already has answers here:
Recursive depth function on an array
(6 answers)
Closed 1 year ago.
I have an array of categories as:
var categories = [
{
name: 'Books',
slug: 'books',
parent_name: null,
},
{
name: 'Fiction',
slug: 'books/fiction',
parent_name: 'Books'
},
{
name: 'Romance fiction',
slug: 'books/fiction/romance',
parent_name: 'Fiction'
},
{
name: 'Bags',
slug: 'bags',
parent_name: null
}
];
I want to make this array into nested array/object so I can render them in dropdowns menu (upto two levels of dropdowns). But do not have any idea how to do it.
Something like this:
var nestedCatefories = [
{
name: 'Books',
slug: 'books',
parent_name: null,
children: [
{
name: 'Fiction',
slug: 'books/fiction',
parent_name: 'books',
children: [
{
name: 'Romance',
slug: 'books/fiction/romance',
parent_name: 'Fiction'
}
]
}
]
},
{
name: 'Bags',
slug: 'bags',
parent_name: null
}
];
How do I change this linear array to nested array?
Reduce the array to a Map, with a predefined root array. For each new item, add it to the Map using it's name as the key. Then get / initialize it's parent's children array, or use root for items without parents. Add the item to it's parent. At the end get the root that hold the entire tree:
const categories = [{"name":"Books","slug":"books","parent_name":null},{"name":"Fiction","slug":"books/fiction","parent_name":"Books"},{"name":"Romance fiction","slug":"books/fiction/romance","parent_name":"Fiction"},{"name":"Bags","slug":"bags","parent_name":null}];
const result = categories.reduce((acc, item) => {
acc.set(item.name, item)
const parent = item.parent_name === null
? acc.get('root')
: (acc.get(item.parent_name).children ??= [])
parent.push(item)
return acc
}, new Map([['root', []]])).get('root')
console.log(result)
Have you tried using a simple Array.map()?
Something like this:
var nestedArr = linearArr.map(obj=>obj.children= [{obj},{obj}])

JavaScript Get Indexes based from the Selected Array of Object Id

Today, I'm trying to get the list of javascript index based from the selected data id that I have.
I'm following this guide from https://buefy.org/documentation/table/#checkable where it needs something like this: checkedRows: [data[1], data[3]] to able to check the specific row in the table.
What I need to do is to check the table based from my web API response.
I have this sample response data.
response.data.checkedRows // value is [{id: 1234}, {id: 83412}]
and I have the sample data from the table.
const data = [{
id: 1234,
name: 'Jojo'
},{
id: 43221,
name: 'Jeff'
},{
id: 83412,
name: 'Kacey'
}]
So basically, I need to have something, dynamically, like this: checkedRows: [data[0], data[2]] because it matches the data from the response.data.checkedRows
So far, I tried using forEach
let selectedIndex = [];
response.data.checkedRows.forEach((d) => {
this.data.forEach((e) => {
if (d.id=== e.id) {
// need the result to be dynamic depending on the response.data.checkedRows
this.checkedRows = [data[0], data[2]]
}
});
});
I'm stuck here because I'm not sure how can I get the index that matches the selected checkedRows from response.
Any help?
Map the response checkedRows, and in the callback, .find the matching object in the array:
const checkedRows = [{id: 1234}, {id: 83412}];
const data = [{
id: 1234,
name: 'Jojo'
},{
id: 43221,
name: 'Jeff'
},{
id: 83412,
name: 'Kacey'
}];
const objs = checkedRows.map(({ id }) => (
data.find(obj => obj.id === id)
));
console.log(objs);
If there are a lot of elements, you can use a Set of the IDs to find instead to decrease the computational complexity:
const checkedRows = [{id: 1234}, {id: 83412}];
const data = [{
id: 1234,
name: 'Jojo'
},{
id: 43221,
name: 'Jeff'
},{
id: 83412,
name: 'Kacey'
}];
const ids = new Set(checkedRows.map(({ id }) => id));
const objs = data.filter(obj => ids.has(obj.id));
console.log(objs);

how to loop through multiple arrays inside an array and filter a value from it-Javascript

I'm using EXTJS framework for my code.
below is my array structure:
data = [{
id: 22,
rows: [{
id: "673627",
name: "ABS",
address: "536street"
}, {
id: "333",
name: "TEST$$",
address: "536street"
}, {
id: "999",
name: "TEST$$",
address: "536street"
}]
}, {
id: 33,
rows: [{
id: "899",
name: "TES",
address: "536street"
}, {
id: "333",
name: "TEST$$",
address: "536street"
}, {
id: "999",
name: "TES673",
address: "536street"
}]
}]
Now I want to filter the name from this array, whose value I'm comparing with say "TEST$$".
I'm doing this;
Ext.each(data, function(item) {
filter = item.rows.filter(function(name) {
return name.name === "TEST$$";
}, this);
}, this);
console.log(filter);
In this case, it returns only 1 match, where as I have 3 matches for this particular value. It returns the match from the last item in the data array and hence I dont get all the matching values, any idea how this can be looped to get all values matching?
thx!
You're reassigning the filter variable on every iteration over the data array:
filter = item.rows.filter(function(name) {
return name.name === "TEST$$";
}, this);
On the last iteration, there is only one match, the one with id of 333, so that's the only one that you see after running the Ext.each. Try pushing to an external array that doesn't get overwritten instead:
const testItems = [];
Ext.each(data, function(item) {
const filtered = item.rows.filter(row => row.name === "TEST$$")
testItems.push(...filtered);
});
console.log(testItems);
Note that there's no need to pass along the this context.
Another option is to flatMap to extract all rows to a single array first:
const output = data
.flatMap(({ rows }) => rows)
.filter(({ name }) => name === 'TEST$$');

How to build tree array from flat array of object with category and subCategrie properties

I am trying to build tree array from flat array, each item in the flat array has two property need to be used to build the tree array, they are 1. category. 2. subCategrie which is array of string.
let data = [
{
id: 1,
name: "Zend",
category: "php",
subCategory: ["framework"]
},
{
id: 2,
name: "Laravel",
category: "php",
subCategory: ["framework"]
},
{
id: 3,
name: "Vesion 5",
category: "php",
subCategory: ["versions"]
},
{
id: 4,
name: "Angular",
category: "frontend",
subCategory: ["framework", "typescript"]
},
{
id: 5,
name: "Aurelia",
category: "frontend",
subCategory: ["framework", "typescript"]
},
{
id: 6,
name: "JQuery",
category: "frontend",
subCategory: []
}
];
It should be
let tree = [
{
name: "php",
children: [
{
name: "framework",
children: [
{
id: 1,
name: "Zend"
},
{
id: 2,
name: "Laravel"
}
]
},
{
name: "versions",
children: [
{
id: 3,
name: "Vesion 5"
}
]
}
]
}
// ...
];
Is there any article, link solving similar problem?
I gave it many tries but stuck when trying to build the sub categories children.
Here's my last attempt which throws error and I know it's wrong but it's for the ones who want to see my attempts
const list = require('./filter.json')
let tree = {};
for (let filter of list) {
if (tree[filter.category]) {
tree[filter.category].push(filter);
} else {
tree[filter.category] = [filter];
}
}
function buildChildren(list, subcategories, category, index) {
let tree = {}
for (let filter of list) {
if (filter.subcategory.length) {
for (let i = 0; i < filter.subcategory.length; i++) {
let branch = list.filter(item => item.subcategory[i] === filter.subcategory[i]);
branch.forEach(item =>{
if (tree[filter.subcategory[i]]){
tree[filter.subcategory[i]] = tree[filter.subcategory[i]].push(item)
}else{
tree[item.subcategory[i]] = [item]
}
})
}
}
}
console.log('tree ', tree);
}
Heads up, For javascript I usually use Lodash (usually written as _ in code) but most of these methods should also be built in to the objects in javascript (i.e. _.forEach = Array.forEach())
const tree = [];
// First Group all elements of the same category (PHP, Frontend, etc.)
data = _.groupBy(data, 'category');
_.forEach(data, function (categoryElements, categoryName) {
// Each Category will have it's own subCategories that we will want to handle
let categorySubCategories = {};
// The categoryElements will be an array of all the objects in a given category (php / frontend / etc..)
categoryElements.map(function (element) {
// For each of these categoryies, we will want to grab the subcategories they belong to
element.subCategory.map(function (subCategoryName) {
// Check if teh category (PHP) already has already started a group of this subcategory,
// else initialize it as an empty list
if (!categorySubCategories[subCategoryName]) { categorySubCategories[subCategoryName] = []; }
// Push this element into the subcategory list
categorySubCategories[subCategoryName].push({id: element.id, name: element.name});
});
});
// Create a category map, which will be a list in the format {name, children}, created from
// our categorySubCategories object, which is in the format {name: children}
let categoryMap = [];
_.forEach(categorySubCategories, function (subCategoryElements, subCategoryName) {
categoryMap.push({name: subCategoryName, children: subCategoryElements});
});
// Now that we've grouped the sub categories, just give the tree it's category name and children
tree.push({name: categoryName, children: categoryMap});
});
};
The key to success here is to create an interim format that allows for easy lookups. Because you work with children arrays, you end up having to use filter and find whenever you add something new, to prevent duplicates and ensure grouping.
By working with a format based on objects and keys, it's much easier to do the grouping.
We can create the groups in a single nested loop, which means we only touch each item once for the main logic. The group has this format:
{ "categoryName": { "subCategoryName": [ { id, name } ] } }
Then, getting to the required { name, children } format is a matter of one more loop over the entries of this tree. In this loop we move from { "categoryName": catData } to { name: "categoryName", children: catData }
Here's an example that shows the two steps separately:
const data=[{id:1,name:"Zend",category:"php",subCategory:["framework"]},{id:2,name:"Laravel",category:"php",subCategory:["framework"]},{id:3,name:"Vesion 5",category:"php",subCategory:["versions"]},{id:4,name:"Angular",category:"frontend",subCategory:["framework","typescript"]},{id:5,name:"Aurelia",category:"frontend",subCategory:["framework","typescript"]},{id:6,name:"JQuery",category:"frontend",subCategory:[]}];
// { category: { subCategory: [ items ] } }
const categoryOverview = data.reduce(
(acc, { id, name, category, subCategory }) => {
// Create a top level group if there isn't one yet
if (!acc[category]) acc[category] = {};
subCategory.forEach(sc => {
// Create an array for this subCat if there isn't one yet
acc[category][sc] = (acc[category][sc] || [])
// and add the current item to it
.concat({ id, name });
});
return acc;
},
{}
)
const nameChildrenMap = Object
.entries(categoryOverview)
// Create top level { name, children } objects
.map(([cat, subCats]) => ({
name: cat,
children: Object
.entries(subCats)
// Create sub level { name, children } objects
.map(([subCat, items]) => ({
name: subCat,
children: items
}))
}))
console.log(nameChildrenMap);

Filter array of objects to a single matching object in mongodb/meteor

I have a query which looks as so:
var writer = Writers.findOne({
_id: writerId,
books: {
$elemMatch: {
id: Books.findOne({ slug: bookSlug })._id
}
}
});
However, this will return the full list of classes in the query.
{
name: "H.P. Lovecraft",
books: [{
id: "1234",
slug: "at-the-mountains-of-madness"
}, {
id: "5678",
slug: "herbert-west-reanimator"
}]
}
Would there be a way to eliminate all information except for the one item in the list I want and make it into an object? That is to say, I want my final result to be:
{
name: "H.P Lovecraft",
book: {
id: "1234",
slug: "herbert-west-reanimator"
}
}
How would this be done in Meteor with mongodb?
One approach you could take is to use the $elemMatch projection operator with the findOne() query. For the document with _id equal to writerId, the $elemMatch projection returns only the first matching element from the array:
var bookId = Books.findOne({ slug: bookSlug })._id,
writer = Writers.findOne({ _id: writerId },
{ books: { $elemMatch: { id: bookId } },
_id: 0,
name: 1
}
);
Another approach would be to use Underscore library's _.find() method to return the specific array element:
var bookId = Books.findOne({ slug: bookSlug })._id,
writer = Writers.findOne({
_id: writerId,
books: {
$elemMatch: {
id: bookId
}
}
}),
book = _.find(writer.books, function(book) {return book.id === bookId});

Categories

Resources