How do I output the child array correctly? [duplicate] - javascript

This question already has answers here:
Build tree array from flat array in javascript
(34 answers)
Closed 1 year ago.
How do I output the child array correctly?
const arr = [
[{ id: 0, parrentId: null, title: "Main tab", parrent: true }],
[{ id: 1, parrentId: 0, title: "Main child 1", parrent: false }],
[{ id: 2, parrentId: 0, title: "Main child 2", parrent: false }],
[{ id: 3, parrentId: 2, title: "Main tab 2", parrent: true }],
[{ id: 4, parrentId: 3, title: "Main child tab 2", parrent: false }]
];
How to output arrays according to child > parrent? But so that the id is parrentId === id
Main tab:
1) Main child 1
2) Main child 2
1) Main tab 2
1)Main child tab 2
I'm trying to
if (arr) {
const child = arr.filter(({ hasChild }) => hasChild);
const notChild = arr.filter(({ hasChild }) => !hasChild);
const test = { ...child, notChild };
console.log(test);
}

If I understood correctly, maybe it is something like this:
const arr = [
[{ id: 0, parentId: null, title: "Main tab", parent: true }],
[{ id: 1, parentId: 0, title: "Main child 1", parent: false }],
[{ id: 2, parentId: 0, title: "Main child 2", parent: false }],
[{ id: 3, parentId: 2, title: "Main tab 2", parent: true }],
[{ id: 4, parentId: 3, title: "Main child tab 2", parent: false }]
];
arr.reduce((acc, data) => {
if(data[0].parent) {
acc = acc.concat(data[0]);
} else {
acc = acc.map(ac => {
if(ac.id === data[0].parentId) {
return {
...ac,
child: ac.child ? ac.child.concat(data[0]) : [].concat(data[0])
};
} else {
return ac;
}
});
}
return acc;
}, []);

Related

How to flatten an object that has array of objects in Javascript

I am trying to solve this question it needs me to flatten this object parent that it has children each parent has 2 children, and each child has 2 children and so on....
My goal is to flatten this to one single object.
const par = {
id: 1,
name: "parent",
children: [{
id: 2,
name: "child 1",
children:[{
id: 4,
name: "child 3",
children: [],
},{
id: 5,
name: "child 4 ",
}]
},{
id: 3,
name: "child 2",
children: [{
id: 6,
name: "child 5",
},{
id: 7,
name: "child 6",
children: []
}]
}]
}
I tried function, but it returns an array from
Deep Flatten JavaScript Object Recursively
function flat(r, a) {
let b = {};
Object.keys(a).forEach(function (k) {
if (k !== 'children') {
b[k] = a[k];
}
});
r.push(b);
if (Array.isArray(a.children)) {
b.children = a.children.map(function (a) { return a.id;});
return a.children.reduce(flat, r);
}
return r;
}
You still owe us a description of your desired output. But if you want something as simple as this:
[
{id: 1, name: "parent"},
{id: 2, name: "child 1"},
{id: 4, name: "child 3"},
{id: 5, name: "child 4"},
{id: 3, name: "child 2"},
{id: 6, name: "child 5"},
{id: 7, name: "child 6"}
]
Then a depth-first recursive function can be as simple as this:
const flatten = ({children = [], ...rest}) => [rest, ...children .flatMap (flatten)]
const par = {id: 1, name: "parent", children: [{id: 2, name: "child 1", children: [{id: 4, name: "child 3", children: []}, {id: 5, name: "child 4 ", }]}, {id: 3, name: "child 2", children: [{id: 6, name: "child 5", }, {id: 7, name: "child 6", children: []}]}]}
console .log (flatten (par))
.as-console-wrapper {max-height: 100% !important; top: 0}
If you wanted to include a parentId field, using null for root-level objects, it's only slightly more complex:
const flatten = ({id, children = [], ...rest}, parentId = null) => [
{id, ...rest, parentId}, ...children .flatMap (c => flatten(c, id))
]
Here's an effective technique using a recursive generator flat -
function *flat({ children = [], ...t }, parentId = null) {
yield { ...t, parentId }
for (const child of children)
yield *flat(child, t.id)
}
const par = {id: 1,name: "parent",children: [{id: 2,name: "child 1",children:[{id: 4,name: "child 3",children: [],},{id: 5,name: "child 4 ",}]},{id: 3,name: "child 2",children: [{id: 6,name: "child 5",},{id: 7,name: "child 6",children: []}]}]}
console.log(Array.from(flat(par)))
.as-console-wrapper { min-height: 100%; top: 0; }
You can collect all the results of a generator using Array.from -
[
{
"id": 1,
"name": "parent",
"parentId": null
},
{
"id": 2,
"name": "child 1",
"parentId": 1
},
{
"id": 4,
"name": "child 3",
"parentId": 2
},
{
"id": 5,
"name": "child 4 ",
"parentId": 2
},
{
"id": 3,
"name": "child 2",
"parentId": 1
},
{
"id": 6,
"name": "child 5",
"parentId": 3
},
{
"id": 7,
"name": "child 6",
"parentId": 3
}
]
Or you can simply iterate thru the generator's result directly -
for (const flatNode of flat(par)) {
// do something with flatNode ...
}
See this related Q&A for a technique to convert the flat tree back to a recursive tree or graph.
You can try this
function flatTree(tree, parentId = null) {
const { id, name, children } = tree;
const result = [{ id, name, parentId }];
if (Array.isArray(children)) {
children.forEach((child) => {
result.push(...flatTree(child, id));
});
}
return result;
}
const par = {
id: 1,
name: "parent",
children: [
{
id: 2,
name: "child 1",
children: [
{
id: 4,
name: "child 3",
children: [],
},
{
id: 5,
name: "child 4 ",
},
],
},
{
id: 3,
name: "child 2",
children: [
{
id: 6,
name: "child 5",
},
{
id: 7,
name: "child 6",
children: [],
},
],
},
],
};
console.log(flatTree(par));
/**
* Output:
* [
{ id: 1, name: 'parent', parentId: null },
{ id: 2, name: 'child 1', parentId: 1 },
{ id: 4, name: 'child 3', parentId: 2 },
{ id: 5, name: 'child 4 ', parentId: 2 },
{ id: 3, name: 'child 2', parentId: 1 },
{ id: 6, name: 'child 5', parentId: 3 },
{ id: 7, name: 'child 6', parentId: 3 }
]
*/
Here is a solution using object-scan. Reinventing the wheel is typically not as bug-free, flexible or maintainable as using a battle-tested library!
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan#18.4.0/lib/index.min.js';
const par = { id: 1, name: 'parent', children: [{ id: 2, name: 'child 1', children: [{ id: 4, name: 'child 3', children: [] }, { id: 5, name: 'child 4 ' }] }, { id: 3, name: 'child 2', children: [{ id: 6, name: 'child 5' }, { id: 7, name: 'child 6', children: [] }] }] };
const fn = objectScan(['**{children[*]}.id'], {
rtn: ({ parent: { id, name } }) => ({ id, name })
});
const r = fn(par);
console.log(r);
/* => [
{ id: 7, name: 'child 6' },
{ id: 6, name: 'child 5' },
{ id: 3, name: 'child 2' },
{ id: 5, name: 'child 4 ' },
{ id: 4, name: 'child 3' },
{ id: 2, name: 'child 1' },
{ id: 1, name: 'parent' }
] */
</script>
Disclaimer: I'm the author of object-scan

Is there a way to filter an item in a array inside another array

Basicaly I have an object that has an array in it and I have a superior array of this object.
The object is the following.
category: {
name,
items: [{
id,
name,
price,
image,
}],
}
This array can look like:
[{
name: "Category 1",
items: [{
id: 1,
name: "Item 1 of category 1",
price: 12.34,
image: "/path/to/image",
},
{
id: 2,
name: "Item 2 of category 1",
price: 56.78,
image: "/path/to/image2",
}]
},
{
name: "Category 2",
items: [{
id: 3,
name: "Item 1 of category 2",
price: 87.65,
image: "/path/to/image3",
},
{
id: 4,
name: "Item 2 of category 1",
price: 43.21,
image: "/path/to/image4",
}]
}]
My question is, it's possible to search for the id since I have an array with all of this data.
Currently I am solving the problem with the following code:
var price = 0;
const menu = [];
state.user.menu.map((item, k) => {
item.category.items.forEach((itm) => menu.push(itm));
return null;
});
state.user.items.forEach((item) => {
price += menu.filter((itm) => itm.id === item.id)[0].price * item.quantity;
});
This basicaly copies every item in the array inside of the object and ignores the category name so I only have a big array.
For what I need now, I need to correlate each item with the category name, so, I can't do as it is now.
Basically, I have a list of Ids and I need to display them with the corresponding category that is in this array.
(Items to search)
[{
timestamp: "123456",
userid: "123456",
...
id: 1,
price: 12.34,
},
{
timestamp: "123456",
userid: "123456",
...
id: 3,
price: 87.65,
},
{
timestamp: "123456",
userid: "123456",
...
id: 4,
price: 43.21,
}]
(Expected Result)
[{
name: "Category 1",
items: [{
id: 1,
name: "Item 1 of category 1",
price: 12.34,
image: "/path/to/image",
}]
},
{
name: "Category 2",
items: [{
id: 3,
name: "Item 1 of category 2",
price: 87.65,
image: "/path/to/image3",
},
{
id: 4,
name: "Item 2 of category 1",
price: 43.21,
image: "/path/to/image4",
}]
}]
Any sugestions is welcome, thanks.
const data = [
{ name: "Category 1", items: [{ id: 1, name: "Item 1 of category 1", price: 12.34, image: "/path/to/image" }, { id: 2, name: "Item 2 of category 1", price: 56.78, image: "/path/to/image2" }] },
{ name: "Category 2", items: [{ id: 3, name: "Item 1 of category 2", price: 87.65, image: "/path/to/image3" }, { id: 4, name: "Item 2 of category 1", price: 43.21, image: "/path/to/image4" }] }
];
const getItemsById = (arr = []) => {
// get list of ids to search for
const ids = arr.map(({ id }) => id);
// iterate over list of objects
return data.reduce((list, elem) => {
// get current items with ids
const items = elem.items.filter(({ id }) => ids.includes(id));
// if any found, add element with filtered list
if(items.length > 0) list.push({ ...elem, items });
return list;
}, []);
}
console.log( getItemsById([{ id: 1 }, { id: 3 }, { id: 4 }]) );
Is this what you're looking for?
const obj = {
category: {
name: "Test",
items: [{
id: 1,
name: "Test",
price: 50,
image: "Test",
}],
}
}
console.log(obj.category.items[0].id);

Push data to parent in a recursive function

I have the following object:
let model = {
id: 1,
name: "model 1",
children: [{
id: 2,
name: "sub model 1",
children: [{
id: 3,
name: "criteria 1",
isCriteria: true,
answer: {
mark: 4
}
},
{
id: 4,
name: "criteria 2",
isCriteria: true
}
]
},
{
id: 5,
name: "sub model 2",
children: [{
id: 6,
name: "criteria 3",
isCriteria: true,
answer: {
mark: 4
}
},
{
id: 7,
name: "criteria 4",
isCriteria: true,
answer: {
mark: 2
}
}
]
}
]
};
I want in result the following object:
{
name: "model 1",
answer: {
mark: 3.5,
completion: 75
},
children: [{
name: "sub model 1",
answer: {
mark: 4,
completion: 50
},
children: [{
name: "criteria 1",
isCriteria: true,
answer: {
mark: 4
}
},
{
name: "criteria 2",
isCriteria: true
}
]
},
{
name: "sub model 2",
answer: {
mark: 3,
completion: 100
},
children: [{
name: "criteria 3",
isCriteria: true,
answer: {
mark: 4
}
},
{
name: "criteria 4",
isCriteria: true,
answer: {
mark: 2
}
}
]
}
]
}
EXPLICATION:
I want to push to every parent the following answer object:
{
mark: the sum of marks of all children/total of children (exclude children with no answer),
completion: (the sum of children with answer/total children) * 100
}
!! NB: The depth of the object is unknown.
I tried the following function, But it adds the answer object only to the first parent before last depth
function loopThoughModelChildren(node, parent) {
if (node == null) {
return;
};
if (node.isCriteria) {
if (!parent.tempAnswer) {
parent.tempAnswer = [];
}
parent.tempAnswer.push({ child: node.id, answer: node.answer });
}
if (node.children)
node.children.forEach(child => loopThoughModelChildren(child, node));
}
You could take a recursive approach with a look to the children property.
Then generate new nodes and assign the answers.
function convert(node) {
if (!node.children) return node;
var marks = 0,
completions = 0,
count = 0,
children = node.children.map(node => {
node = convert(node);
if (node.answer) {
marks += node.answer.mark;
completions += node.answer.completion || 100;
count++;
}
return node;
});
return { ...node, answer: { mark: marks / count, completion: completions / node.children.length }, children };
}
var model = { id: 1, name: "model 1", children: [{ id: 2, name: "sub model 1", children: [{ id: 3, name: "criteria 1", isCriteria: true, answer: { mark: 4 } }, { id: 4, name: "criteria 2", isCriteria: true }] }, { id: 5, name: "sub model 2", children: [{ id: 6, name: "criteria 3", isCriteria: true, answer: { mark: 4 } }, { id: 7, name: "criteria 4", isCriteria: true, answer: { mark: 2 } }] }] },
result = convert(model);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

typescript- filter array of objects based on another array

I have an array of objects like given below
readonly allItems = [
{
id: 0,
title: "Item 0",
belongsTo: 'admin'
},
{
id: 1,
title: "Item 1",
belongsTo: 'user'
},
{
id: 2,
title: "Item 2",
belongsTo: 'all'
},
{
id: 3,
title: "Item 3",
belongsTo: 'user'
},
{
id: 4,
title: "Item 4",
belongsTo: 'all'
}
];
And I have an array of numbers like given below
let selItems = [0,2,4];
What I'm trying to do is, filter the allItems array based on selItems array
For doing that, I wrote the following code, which is obviously wrong.
for(let i=0; i< this.allItems.length; i++){
if(selItems.includes(this.allItems[i].id)){
tempMenu.push(this.allItems[i]);
}
console.log(tempMenu);
}
I'm getting the following as output
[{
id: 0,
title: "Item 0",
belongsTo: 'admin'
}]
The result I'm expecting is like this:
[
{
id: 0,
title: "Item 0",
belongsTo: 'admin'
},
{
id: 2,
title: "Item 2",
belongsTo: 'all'
},
{
id: 4,
title: "Item 4",
belongsTo: 'all'
}
]
Can anyone show me the right way to do this?
Thanks!
You might use .map instead:
const allItems = [{
id: 0,
title: "Item 0",
belongsTo: 'admin'
},
{
id: 1,
title: "Item 1",
belongsTo: 'user'
},
{
id: 2,
title: "Item 2",
belongsTo: 'all'
},
{
id: 3,
title: "Item 3",
belongsTo: 'user'
},
{
id: 4,
title: "Item 4",
belongsTo: 'all'
}
];
const selItems = [0, 2, 4];
const output = selItems.map(num => allItems.find(({ id }) => id === num));
console.log(output);
To reduce computational complexity to O(N) instead of O(N^2), you can transform it into an object indexed by id first:
const allItems = [{
id: 0,
title: "Item 0",
belongsTo: 'admin'
},
{
id: 1,
title: "Item 1",
belongsTo: 'user'
},
{
id: 2,
title: "Item 2",
belongsTo: 'all'
},
{
id: 3,
title: "Item 3",
belongsTo: 'user'
},
{
id: 4,
title: "Item 4",
belongsTo: 'all'
}
];
const selItems = [0, 2, 4];
const allItemsById = allItems.reduce((a, item) => {
a[item.id] = item;
return a;
}, {});
const output = selItems.map(num => allItemsById[num]);
console.log(output);
Or with filter:
const allItems = [{
id: 0,
title: "Item 0",
belongsTo: 'admin'
},
{
id: 1,
title: "Item 1",
belongsTo: 'user'
},
{
id: 2,
title: "Item 2",
belongsTo: 'all'
},
{
id: 3,
title: "Item 3",
belongsTo: 'user'
},
{
id: 4,
title: "Item 4",
belongsTo: 'all'
}
];
const selItemsSet = new Set([0, 2, 4]);
const output = allItems.filter(({ id }) => selItemsSet.has(id));
console.log(output);

Finding a path to target object in nested array of objects in Javascript

I'm currently working through a problem that I'm having some trouble figuring out where I need to find a child node in an array of objects. The target could be one or many levels deep.
The issue is, once I find the object, I also need to push the path I took to get to that object into the resulting data array.
Currently, I have written code that can successfully find the child node:
const buildFullTree = (tree, cat, data = []) => {
let collection = [tree]
while (collection.length) {
let node = collection.shift()
if (node.id === cat.id) {
data.push(node)
}
collection.unshift(...node.children)
}
return data
}
However, this isn't sufficient in terms of getting the path taken to that object.
I'm pretty sure that I need to change this to a recursive depth-first search solution in order to achieve what I'm looking for, but I am not sure how to change the while loop to accomplish this.
If I understand your question correctly, then perhaps you could revise your path search function like so to achieve what you require:
const buildFullTree = (departmentTree, category, data = []) => {
const findPath = (node, category) => {
//If current node matches search node, return tail of path result
if (node.id === category.id) {
return [node]
} else {
//If current node not search node match, examine children. For first
//child that returns an array (path), prepend current node to that
//path result
for (const child of node.children) {
const childPath = findPath(child, category)
if (Array.isArray(childPath)) {
childPath.unshift(child)
return childPath
}
}
}
}
const foundPath = findPath(departmentTree, category)
// If search from root returns a path, prepend root node to path in
// data result
if (Array.isArray(foundPath)) {
data.push(departmentTree)
data.push(...foundPath)
}
return data
}
const departmentTree = {
id: 5,
title: 'department',
level: 1,
children: [{
id: 1,
parentId: 5,
title: 'category',
level: 2,
children: [{
id: 15,
parentId: 1,
title: 'subcategory',
level: 3,
children: []
}, {
id: 18,
parentId: 1,
level: 3,
title: 'subcategory',
children: []
}, {
id: 26,
parentId: 1,
level: 3,
title: 'subcategory',
children: [{
id: 75,
parentId: 26,
level: 4,
title: 'sub-subcategory',
children: []
}, {
id: 78,
parentId: 26,
level: 4,
title: 'sub-subcategory',
children: []
}]
}]
}, {
id: 23823,
title: 'category',
level: 2,
children: []
}, {
id: 9,
parentId: 5,
level: 2,
title: 'category',
children: [{
id: 48414,
parentId: 9,
level: 3,
title: 'subcategory',
children: []
}, {
id: 2414,
parentId: 9,
level: 3,
title: 'subcategory',
children: []
}, {
id: 42414,
parentId: 9,
level: 3,
title: 'subcategory',
children: [{
id: 2323213,
parentId: 42414,
level: 4,
title: 'sub-subcategory',
children: []
}, {
id: 322332,
parentId: 42414,
level: 4,
title: 'sub-subcategory',
children: []
}]
}]
}]
};
console.log('Path to 2323213:',
buildFullTree(departmentTree, {
id: 2323213
}).map(node => node.id).join(' -> '))
console.log('Path to 23823:',
buildFullTree(departmentTree, {
id: 23823
}).map(node => node.id).join(' -> '))
console.log('Path to -1 (non existing node):',
buildFullTree(departmentTree, {
id: -1
}).map(node => node.id).join(' -> '))

Categories