Map through array of arrays, to find id and change object - javascript

I have an array of ids ['id1', 'id3'].
I also have an array of items:
[
{
children: [{
children: [{
id: "id1", //This is value I need to find
status: { state: false}, //this is value I need to change
}],
}],
},
{
children: [{
children: [{
id: "id2",
status: { state: false},
}],
}],
},
{
children: [{
children: [{
id: "id3",
status: { state: false},
}],
}],
},
]
My goal is to find every item by id from first array, and change attribute state, then return all items having included those I have changed.
This was my try, but it returns all items again, also Im not sure how to change the attribute.
items.filter(item =>
item.children.map(child =>
child.children.map(object =>
idsArray.map(id => id === object.id)
)))

I think you can use recursive function something like below :
let ids = ["id1", "id2"];
let arrayOfItems = [
{
children: [
{
children: [
{
id: "id1",
status: {
state: false
}
}
]
}
]
},
{
children: [
{
children: [
{
id: "id2",
status: {
state: false
}
}
]
}
]
},
{
children: [
{
children: [
{
id: "id3",
status: {
state: false
}
}
]
}
]
}
];
function changeStatus(arrayOfItems, ids) {
return arrayOfItems.map((e) => {
if (e.id && ids.includes(e.id)) {
return { ...e, status: { state: true } };
} else if (e.children) {
return { ...e, children: changeStatus(e.children, ids) };
} else {
return { ...e };
}
});
}
console.log(changeStatus(arrayOfItems,ids));

Related

How to split recursive array into two categories based on specific field and also get the parent path?

I have confusion on recursive function to split recursive array into another recursive arrays. Let say I have an item and inside it there is packaged items (or children), and the children also have an item and children, there's no limitation about the recursion level.
The problem
I need to separate the paths of package item object that has isOptional true and paths of isOptional false. The function should return 2 categorized array and the value inside the array must be recursive just like the input structure. Please check the diagram below
Here's input example
const product = {
name: "Product 1",
packagedItems: [
{
id: 1,
isOptional: false,
item: {
name: "1#1",
packagedItems: [
{
id: 3,
isOptional: false,
item: {
name: "2#1",
packagedItems: [
{
id: 5,
isOptional: false,
item: {
name: "3#1",
packagedItems: []
}
},
{
id: 6,
isOptional: true,
item: {
name: "3#2",
packagedItems: []
}
}
]
}
}
]
}
},
{
id: 2,
isOptional: false,
item: {
name: "1#2",
packagedItems: [
{
id: 4,
isOptional: false,
item: {
name: "2#2",
packagedItems: [
{
id: 7,
isOptional: true,
item: {
name: "3#3",
packagedItems: []
}
},
{
id: 8,
isOptional: true,
item: {
name: "3#4",
packagedItems: []
}
}
]
}
}
]
}
}
]
};
Here's the diagram
enter image description here
What I've tried is only able to get parent's name but not building the same structure as the input
const getParents = (
packagedItems: PackagedItem[],
ancestors: (string | PackagedItem)[] = []
): any => {
for (let pack of packagedItems) {
if (pack.isOptional && !pack.item.packagedItems.length) {
return ancestors.concat(pack);
}
const found = getParents(
pack.item.packagedItems,
ancestors.concat(pack.item.name)
);
if (found) {
return found;
}
}
return undefined;
};
console.log(getParents(product.packagedItems));
only return
[
"1#1",
"2#1",
{
id: 6
isOptional: true
item: Object
}
]
Expected result would be two arrays like this.
const optionalTrue = [
{
id: 1,
isOptional: false,
item: {
name: "1#1",
packagedItems: [
{
id: 3,
isOptional: false,
item: {
name: "2#1",
packagedItems: [
{
id: 6,
isOptional: true,
item: {
name: "3#2",
packagedItems: []
}
}
]
}
}
]
}
},
{
id: 2,
isOptional: false,
item: {
name: "1#2",
packagedItems: [
{
id: 4,
isOptional: false,
item: {
name: "2#2",
packagedItems: [
{
id: 7,
isOptional: true,
item: {
name: "3#3",
packagedItems: []
}
},
{
id: 8,
isOptional: true,
item: {
name: "3#4",
packagedItems: []
}
}
]
}
}
]
}
}
];
const optionalFalse = [
{
id: 1,
isOptional: false,
item: {
name: "1#1",
packagedItems: [
{
id: 3,
isOptional: false,
item: {
name: "2#1",
packagedItems: [
{
id: 5,
isOptional: false,
item: {
name: "3#1",
packagedItems: []
}
}
]
}
}
]
}
}
];
Your function is going in the right direction, but the pack is a leaf, and matches the category, you should not continue with the recursive call. Also the collection into the ancestors array will not work well as you collect either pack or name in it.
Here is an implementation that gives the output you intended:
function filterOnLeaf(item, optional) {
return {
...item,
packagedItems: item.packagedItems.map(package => {
if (!package.item.packagedItems.length) { // Leaf
if (package.isOptional != optional) return null;
} else { // Internal
package = {
...package,
item: filterOnLeaf(package.item, optional)
};
if (!package.item.packagedItems.length) return null;
}
return package;
}).filter(Boolean)
};
}
const product = {name: "Product 1",packagedItems: [{id: 1,isOptional: false,item: {name: "1#1",packagedItems: [{id: 3,isOptional: false,item: {name: "2#1",packagedItems: [{id: 5,isOptional: false,item: {name: "3#1",packagedItems: []}},{id: 6,isOptional: true,item: {name: "3#2",packagedItems: []}}]}}]}},{id: 2,isOptional: false,item: {name: "1#2",packagedItems: [{id: 4,isOptional: false,item: {name: "2#2",packagedItems: [{id: 7,isOptional: true,item: {name: "3#3",packagedItems: []}},{id: 8,isOptional: true,item: {name: "3#4",packagedItems: []}}]}}]}}]};
const optionalTrue = filterOnLeaf(product, true);
const optionalFalse = filterOnLeaf(product, false);
console.log("optional is true:");
console.log(optionalTrue);
console.log("optional is false:");
console.log(optionalFalse);

Append array of objects by checking the nesting level: Javascript

I have an nested array of objects with the following format
const arr1 = [
{
name: "internalcorp.com",
children: [
{
name: "internalcorp.com.child1",
children: [
{
name: "internalcorp.com.grandchild1",
children: []
},
{
name: "internalcorp.com.grandchild2",
children: []
}
]
},
{
name: "internalcorp.com.child2",
children: []
}
]
},
{
name: "internalcorpwebsite.com",
children: [
{
name: "internalcorpwebsite.com.child1",
children: []
}
]
}
];
Need to add an className property to the each array object based on its level. Like for example, parent should have level-0 group and children items should have level-x leaf where x is the level number relative to the main parent.
Output should look like
const result = [
{
name: "internalcorp.com",
className: "level-0 group",
children: [
{
name: "internalcorp.com.child1",
className: "level-1 leaf",
children: [
{
name: "internalcorp.com.grandchild1",
className: "level-2 leaf",
children: []
},
{
name: "internalcorp.com.grandchild2",
className: "level-2 leaf",
children: []
}
]
},
{
name: "internalcorp.com.child2",
className: "level-1 leaf",
children: []
}
]
},
{
name: "internalcorpwebsite.com",
className: "level-0 group",
children: [
{
name: "internalcorpwebsite.com.child1",
className: "level-1 leaf",
children: [],
}
],
}
];
Code that I have tried
const result = arr1.map((item,idx)=> {
if(item.children.length){
return {
...item,
className: `level${idx} leaf`
}
}
})
You can use the power of recursion:
function recursive(item, id) {
let returnedItem = item
if (item.children.length) {
returnedItem = {
...returnedItem,
children: returnedItem.children.map(childItem => recursive(childItem, id + 1))
}
}
if (0 === id) {
return { ...returnedItem, className: `level-0 group` }
}
return { ...returnedItem, className: `level-${id} leaf` }
}
const result = arr1.map(item => recursive(item, 0))

How to group object inside array

Here is what I have
[
{
typeProgramId: {
name: 'type1',
},
title: 'test1',
},
{
typeProgramId: {
name: 'type1',
},
subTypeProgramId: [{
name: 'sub1',
}],
title: 'test2',
},
{
typeProgramId: {
name: 'type2',
},
title: 'test3',
},
{
typeProgramId: {
name: 'type2',
},
subTypeProgramId: {
name: 'sub2',
},
title: 'test4',
}
]
First I want to group typeProgramId if the title have the same typeProgramId I want to push title into array by each typeProgramId but If the data have typeProgramId and subTypeProgram Id I want to group subtypeProgramId in typeProgramId too. if not empty subtypeProgramId I want to push it in array title inside subtypeProgram Id. I try to use lodash groupBy and many way but it still did not work.
Here is what I want
{
typeProgramId: [{
name: 'type1',
title: [
'test1',
],
subTypeProgramId: {
name: sub1,
title: [
'test2'
]
}
}, {
name: 'type2',
title: [
'test3',
],
subTypeProgramId: [{
name: sub1,
title: [
'test4'
]
}
}]
}
what I do now
let result = _.groupBy(getProgram, function(data) {
return data.typeProgramId
})
result = _.map(result, function(group, data) {
// I think in here I must groupBy subTypeProgramId again
// the data return all string not object after group
return {
typeProgramId: data,
titile: group,
}
})
Please check the below code. I have used reduce function of Array. It produces the expected result.
function updateMem(mem, prgIndex, val){
if(prgIndex < 0) {
mem.typeProgramId.push({});
prgIndex = mem.typeProgramId.length - 1;
}
mem.typeProgramId[prgIndex].name = val.typeProgramId.name;
if(val.subTypeProgramId){
mem.typeProgramId[prgIndex].subTypeProgramId = Object.assign({}, mem.typeProgramId[prgIndex].subTypeProgramId || {}, {"name" : val.subTypeProgramId.name, "title": []});
mem.typeProgramId[prgIndex].subTypeProgramId.title.push(val.title);
} else {
mem.typeProgramId[prgIndex].title = (mem.typeProgramId[prgIndex].title ? mem.typeProgramId[prgIndex].title : []);
mem.typeProgramId[prgIndex].title.push(val.title);
}
};
arr.reduce((mem, val) => {
var prgIndex = mem.typeProgramId.findIndex((p) => p.name === val.typeProgramId.name);
updateMem(mem, prgIndex, val);
return mem;
}, {typeProgramId: []});

How to push a new item to nested array in angular7

I have a nested array ,i want to push new item to nested array.
FoodNode= [
{
name: 'Fruit',
parent:'root',
children: [
{name: 'Apple',parent:'Fruit'},
{name: 'Banana',parent:'Fruit'},
{name: 'Fruit loops',parent:'Fruit'},
]
}, {
name: 'Vegetables',
parent:'root',
children: [
{
name: 'Green',
parent:'Vegetables',
children: [
]
}, {
name: 'Orange',
parent:'Vegetables',
children: [
]
},
]
},
];
Now i want to add it to childrens of green.
this.fileElements.filter(res => {
if (res.parent === 'root') {
this.fileList.push(res);
} else {
this.fileList.filter((search, index) => {
if (search.name === res.parent) {
console.log(typeof search.children === 'undefined');
search.children.push(res);
}
});
}
});
this code push the item to 1 level only (like immediate children),bases on parent value Im pushing element to the children.I'm using mat-tree so I need same structure.please help
Hope it helps.
this.FoodNode.forEach(e => {
if (e.name == "Vegetables") {
let abcd = e.children;
abcd.forEach(e1 => {
if (e1.name === "Green") {
let data = [
{ name: "Apple" },
{ name: "Banana" },
{ name: "Fruit loops" }
];
data.forEach(e2 => {
e1["children"].push(e2);
});
}
});
}
});

Find element inside tree and change it's parents' properties values

I want to find an element inside a tree by its property value and then move back to the beginning of the tree and set isExpanded property of each of element's parents to true using JS.
So I have this object:
elementsArray = [
{
name: 'test_1',
isExpanded: false,
expandElements: [
{
name: 'test_1_1',
isExpanded: false,
expandElements: [
{
name: 'test_1_1_1'
},
{
name: 'test_1_1_2'
}
]
},
{
name: 'test_1_2',
isExpanded: false,
expandElements: [
{
name: 'test_1_2_1'
},
{
name: 'test_1_2_2'
}
]
}
]
},
{
name: 'test_2',
isExpanded: false,
expandElements: [
{
name: 'test_2_1',
isExpanded: false,
expandElements: [
{
name: 'test_2_1_1'
},
{
name: 'test_2_1_2'
}
]
},
{
name: 'test_2_2',
isExpanded: false,
expandElements: [
{
name: 'test_2_2_1'
},
{
name: 'test_2_2_2'
}
]
}
]
}
]
I want to find the element, which has property name: 'test_2_2_1' and set isExpanded property of each of its parents to true.
How it should work:
call a method expandParentElements(elementsArray, 'test_2_2_1')
elementsArray changed to:
elementsArray = [
{
name: 'test_1',
isExpanded: false,
expandElements: [
{
name: 'test_1_1',
isExpanded: false,
expandElements: [
{
name: 'test_1_1_1'
},
{
name: 'test_1_1_2'
}
]
},
{
name: 'test_1_2',
isExpanded: false,
expandElements: [
{
name: 'test_1_2_1'
},
{
name: 'test_1_2_2'
}
]
}
]
},
{
name: 'test_2',
isExpanded: **true**,
expandElements: [
{
name: 'test_2_1',
isExpanded: false,
expandElements: [
{
name: 'test_2_1_1'
},
{
name: 'test_2_1_2'
}
]
},
{
name: 'test_2_2',
isExpanded: **true**,
expandElements: [
{
name: 'test_2_2_1'
},
{
name: 'test_2_2_2'
}
]
}
]
}
]
By now i've managed to only recursively move inside tree and find proper element. Here how it looks like:
let expandParentElements = (array, searchName) => {
return array.map(item => {
if (item.name === searchName) {
return item
} else if (item.expandElements) {
return expandParentElements(item.expandElements, searchName)
}
})
}
Here is a function that sets isExpanded on the path to the item. The item itself (e.g. when it is has subitems) will not get isExpanded set to true.
This function does also not clear the isExpanded values that might have already been set to true before calling the function.
const expandParentElements = (array, searchName) =>
array && array.some(item => item.name === searchName ||
expandParentElements(item.expandElements, searchName) && (item.isExpanded = true)
);
// Sample data from question
const elementsArray = [{name: 'test_1',isExpanded: false,expandElements: [{name: 'test_1_1',isExpanded: false,expandElements: [{name: 'test_1_1_1'},{name: 'test_1_1_2'}]},{name: 'test_1_2',isExpanded: false,expandElements: [{name: 'test_1_2_1'},{name: 'test_1_2_2'}]}]},{name: 'test_2',isExpanded: false,expandElements: [{name: 'test_2_1',isExpanded: false,expandElements: [{name: 'test_2_1_1'},{name: 'test_2_1_2'}]},{name: 'test_2_2',isExpanded: false,expandElements: [{name: 'test_2_2_1'},{name: 'test_2_2_2'}]}]}];
expandParentElements(elementsArray, "test_2_2_1");
console.log(elementsArray);
You may want to also use a function to collapse all, i.e. set all isExpanded to false:
const collapseAll = (array) =>
array.filter(item => item.expandElements)
.forEach(item => (item.isExpanded = false, collapseAll(item.expandElements)));
elementsArray.forEach(element => {
let updateParent = element.expandElements.reduce(((prevFound, expandElement) => {
let found = expandElement.expandElements.some(obj => obj.name === "test_1_1_1")
expandElement.isExpanded = found
return prevFound || found
}), false)
element.isExpanded = updateParent
})

Categories