Grouping a object value inside n level nested array - javascript

I have an array like this. How to group all child Ids into an array?
My solution below is not giving me all child elements. Where is the mistake? and suggest me any other ways
const data = {
name: '1',
id: '05f770d5',
child: [
{
name: '2',
id: '0ecfc8e1',
child: [
{
name: '3',
id: '2e1eb75c',
child: [],
},
],
},
{
name: '1c',
id: 'b9ee9864',
child: [
{
name: '8',
id: '575f4760',
child: [],
},
],
},
],
};
let array1 = [];
function sumChild(data) {
data.child.forEach((data) => {
array1.push(data.id);
sumChild(data?.child[0]);
});
return array1;
}
sumChild(data);
console.log(array1);

function sumChild(data) {
data.child?.forEach((data) => {
array1.push(data.id);
sumChild(data);
});
return array1;
}

Related

fillter arrays of objects

i have two arrays.
const department = [
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' },
];
const models = [
{
id: '23',
name: 'model1',
departments: [{ id: '1', name: 'department1' }],
},
{
id: '54',
name: 'model2',
departments: [
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' },
],
},
];
i need to render accordions with department names and accordion details with matching models names. My question is how to filter those arrays to get models
We can map through the departments array, and add a models property that equals the models array, but filtered only to the ones that contain a matching department id.
const departments = [
{ id: "1", name: "department1" },
{ id: "2", name: "department2" },
];
const models = [
{
id: "23",
name: "model1",
departments: [{ id: "1", name: "department1" }],
},
{
id: "54",
name: "model2",
departments: [
{ id: "1", name: "department1" },
{ id: "2", name: "department2" },
],
},
];
const getDepartmentsWithModels = () => {
return departments.map((department) => {
return {
...department,
models: models.filter((model) => {
const modelDepartmentIds = model.departments.map(({ id }) => id);
return modelDepartmentIds.includes(department.id);
}),
};
});
};
console.log(getDepartmentsWithModels());
// [ { id: '1', name: 'department1', models: [ [Object], [Object] ] },
// { id: '2', name: 'department2', models: [ [Object] ] } ]```
I've built some code, which iterates over the departments. For each department it iterates the models and for each model it checks if the department is within the model departments.
const department =
[
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' }
]
const models =
[
{
id: '23',
name: 'model1',
departments: [{ id: '1', name: 'department1' }]
},
{
id: '54',
name: 'model2',
departments: [{ id: '1', name: 'department1' },{ id: '2', name: 'department2' }]
}
]
department.forEach( dep => {
console.log(`Department: ${dep.name}`)
models.forEach(model => {
if (model.departments.find(modelDep => dep.id===modelDep.id)) {
console.log(` Model: ${model.name}`)
}
})
})
If you could change your data objects, then your code could be much smoother.
I've changed your data objects slightly by just reducing the departments in a model to be an array of department id's. This code iterates over the departments. For each department it filters the models and iterates over the filtered models to output them to the console. This is lesser code and provides much better performance.
const department =
[
{ id: '1', name: 'department1' },
{ id: '2', name: 'department2' }
]
const models =
[
{
id: '23',
name: 'model1',
departments: ['1']
},
{
id: '54',
name: 'model2',
departments: ['1', '2']
}
]
department.forEach( dep => {
console.log(`Department: ${dep.name}`)
models.filter(model => model.departments.includes(dep.id)).forEach(model => {
console.log(` Model: ${model.name}`)
})
})
There are two solutions.
Using Array.reduce() --> returns an object where the key is department name and value is an array of the names of matching models:
let data1 = models.reduce((res, curr) => {
curr.departments.forEach(dep => {
if (!res[dep.name]) {
res[dep.name] = [curr.name]
} else {
if (!res[dep.name].includes(curr.name)) {
res[dep.name].push(curr.name);
}
}
})
return res;
}, {});
Using map and filter --> returns an array of kind:
[{department: [names of the models]},...]
let data2 = department.map(dep => {
let matchingModels = models.filter(model => {
return model.departments.filter(modDep => {
return modDep.name === dep.name;
}).length > 0;
}).map(mod => {
return mod.name;
});
return {
department: dep.name,
models: matchingModels
}
});

How to find the length of particular type within object using javascript?

i have object like below,
Example 1
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children: [
{
type_1: {
id: ‘12’,
},
type: 'item1-type',
},
{
children: [
{
id: '1',
type: 'item2',
},
{
id: '2',
type:'item2',
},
{
id:'3',
type: 'item2',
},
]
type: 'item2-type',
},
{
children: [
{
id: '4',
type: 'item2',
},
{
id: '5',
type:'item2',
},
{
id:'6',
type: 'item2',
},
]
type: 'item2-type',
},
]
}
now i want to find the count of "item2" type within children array within children array again.
note that the outer children array can be empty array and the children array within children array may not be present. so the input can be of types like below
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children: [] //empty children array
}
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children:
[ //no children array within
{
type_1: {
id: ‘12’,
},
type: “item1-type”,
},
]
}
how can i find the count of type: "item2" within children array considering example1 input.
so the expected count is 6.
could someone help me with this. thanks. new to programming.
const findAllChildrenOfType = (obj, type) => {
let count = 0;
if (obj.type === type) count++;
if (obj.children) {
obj.children.forEach(child => {
const childCount = findAllChildrenOfType(child, type);
count += childCount;
})
}
return count;
}
console.log(findAllChildrenOfType(input, "item2"))

Get the branch of collection without the sibling elements, searching by property

I have the object with the next structure:
let array = [
{
name: 'Name1',
items: [
{
name: 'Name1.1',
items: [
{ id: '1', name: 'Name1.1.1' },
{ id: '2', name: 'Name1.1.2' },
{ id: '3', name: 'Name1.1.3' },
...
],
},
{
name: 'Name1.2',
items: [
{ id: '4', name: 'Name1.2.1' },
{ id: '5', name: 'Name1.2.2' },
],
},
],
},
{
name: 'Name2',
items: [
{
name: 'Name2.1',
items: [
{ id: '6', name: 'Name2.1.1' },
{ id: '7', name: 'Name2.1.2' },
],
},
],
},
];
I want to get the branch without the sibling elements, searching by id. The desired result is the next structure by id = '4':
let array = [
{
name: 'Name1',
items: [
{
name: 'Name1.2',
items: [
{ id: '4', name: 'Name1.2.1' },
],
},
],
}
];
I could find only the end element of the tree ({ id: '4', name: 'Name1.2.1' }). But I don't understand how to get intermediate structures of the tree.
const test = (data, id) => {
if (!data || !data.length) return null;
for (var j = 0; j < data.length; j++) {
var result = data[j].items
? test(data[j].items, id)
: data[j].id
? data[j].id === id
? data[j]
: undefined
: undefined;
if (result !== undefined) {
return result;
}
}
return undefined;
};
test(array, '4');
You should indeed take a recursive approach, but your function currently can only return an id value (a string) or null or undefined. It never returns an array, yet that is what you expect to get.
When a solution is found as a base case, you need to wrap that solution in an array and plain object, each time you get out of the recursion tree.
Here is a working solution:
function getPath(forest, targetid) {
for (let root of forest) {
if (root.id === targetid) return [root]; // base case
let items = root.items && getPath(root.items, targetid);
if (items) return [{ ...root, items }]; // wrap!
}
}
// Example run:
let array = [{name: 'Name1',items: [{name: 'Name1.1',items: [{ id: '1', name: 'Name1.1.1' },{ id: '2', name: 'Name1.1.2' },{ id: '3', name: 'Name1.1.3' },],},{name: 'Name1.2',items: [{ id: '4', name: 'Name1.2.1' },{ id: '5', name: 'Name1.2.2' },],},],},{name: 'Name2',items: [{name: 'Name2.1',items: [{ id: '6', name: 'Name2.1.1' },{ id: '7', name: 'Name2.1.2' },],},],},];
console.log(getPath(array, '4'));

Getting parent node.name recursion is not working properly

I have an infinite tree:
const Data = [
{
id: '1',
name: 'hello',
children: [
{
id: '2',
name: 'world',
children: [
{
id: '3',
name: 'world',
children: [],
},
{
id: '4',
name: 'world',
children: [],
},
],
},
{
id: '5',
name: 'world',
children: [],
},
],
},
];
What I want to do is get the id and name of the path that leads to "world" and push it in to an array.
For example: the first path would be:
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
]
second:
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
{ id: '3', name: 'world' },
]
And then push those arrays into another array.
So my result would look like this:
const result = [
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
],
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
{ id: '3', name: 'world' },
],
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
{ id: '4', name: 'world' },
],
[
{ id: '1', name: 'hello' },
{ id: '5', name: 'world' },
],
];
I have a recursive function:
const findPath = (input="world", data, visitedStack, dataStack) => {
return data.map((node) => {
visitedStack.push({ id: node.id, name: node.name });
if (node.name.toLowerCase().includes(input.toLowerCase())) {
dataStack.push([...visitedStack]);
}
return findPath(
input,
node.children,
visitedStack,
dataStack
);
});
};
But this is adding on all the paths it has visited, so the last array that is pushed into dataStack will look like this:
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
{ id: '3', name: 'world' },
{ id: '4', name: 'world' },
{ id: '5', name: 'world' },
]
Not sure how to fix this. Or is this an incorrect approach?
The problem is that your visitedStack keeps growing, as you are eventually pushing all nodes unto it. Be aware that all recursive executions of your function get the same visitedStack to work with. So pushing [...visitedStack] is not going to push a path, but all nodes that had been visited before, which after a while do not represent a path any more.
If we stick with your function, then just make sure you don't push on visited permanently, but create a copy of that stack with the extra node, which will remain in the deeper recursion, but will not contaminate the whole rest of the execution. This way that extra node will not be there in the other, sibling paths:
const findPath = (input="world", data, visitedStack, dataStack) => {
return data.map((node) => {
let newStack = visitedStack.concat({ id: node.id, name: node.name });
if (node.name.toLowerCase().includes(input.toLowerCase())) {
dataStack.push(newStack);
}
return findPath(
input,
node.children,
newStack,
dataStack
);
});
};
Call as:
let result = [];
findPath("world", data, [], result);
console.log(result);
Alternative
I would however also address the following:
It is a bit odd that findPath does not return the result, but that the caller needs to provide the array in which the resulting paths should be collected. So I would suggest a function that returns the new array, not requiring the caller to pass that array as argument.
It is not useful to have a default value for a parameter, when other parameters following it, do not have a default value. Because, that means you anyway have to provide values for those other parameters, including the one that could have had a default value.
The paths that are returned still contain multiple references to the same objects. You do copy the objects into new objects, but as that new object sits in visitedStack, it will be reused when pushed potentially several times for deeper paths. So I would suggest making the object copies at the very last moment -- when the path is pushing on the result array.
Instead of repeatedly converting the input to lower case, do this only once.
Here is how you could write it:
function findPath(data, input="world") {
const result = [];
input = input.toLowerCase();
function recur(data, visitedStack) {
for (const node of data) {
const newStack = visitedStack.concat(node);
if (node.name.toLowerCase().includes(input)) {
result.push(newStack.map(o => ({id: o.id, name:o.name})));
}
recur(node.children, newStack);
}
}
recur(data, []);
return result;
}
const data = [{id: '1',name: 'hello',children: [{id: '2',name: 'world',children: [{id: '3',name: 'world',children: [],},{id: '4',name: 'world',children: [],},],},{id: '5',name: 'world',children: [],},],},];
const result = findPath(data);
console.log(result);

Why async.map returns multiple copies of array?

const async = require('async');
const arr = [
{ name: 'john', id: '1' },
{ name: 'Andrie', id: '2' }]
let collectArr = [];
let data = async.mapLimit(arr, 5, async function (input) {
collectArr.push({ name: input.name, id: input.id });
return collectArr;
})
data.then((result) =>{
console.log('success',result);
}).catch(e => console.log('err'));
So here i am providing array to async.mapLimit without callback and expecting promise here.
Expected Output :- [ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ] ,
Got Result :-
[ [ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ],
[ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ] ]
So my question is why it is creating multiple copies of array, how to deal with this?
You are needlessly returning a sub array, and the same array reference each iteration, when all you want is to return the new object.
let data = async.mapLimit(arr, 5, async function (input) {
return { name: input.name, id: input.id };
});
Not sure why you need this to be async

Categories

Resources