Reputation: 385
I've been breaking my head for hours to find out how I can delete objects recursively in multidimensional array based on key value. I need to delete all objects containing exclude: true
in the example array below. I tried looping the items with a recursion function but I'm struggeling with splicing the items out of the array...
// example multidimensional array with objects
let array: Object[] = [
// exclude on level 1
{
name: 'A',
exclude: true,
content: [
{
name: 'A-A',
},
{
name: 'A-B',
exclude: true,
},
{
name: 'A-C',
content: [
{
name: 'A-C-A',
}
]
}
]
},
// exclude on level 2
{
name: 'A',
content: [
{
name: 'A-A',
},
{
name: 'A-B',
exclude: true,
},
{
name: 'A-C',
content: [
{
name: 'A-C-A',
}
]
}
]
},
// exclude on level 2 and 3
{
name: 'A',
content: [
{
name: 'A-A',
},
{
name: 'A-B',
exclude: true,
},
{
name: 'A-C',
content: [
{
name: 'A-C-A',
exclude: true,
}
]
}
]
}
]
// run function
deleteItems(array);
// path is an array containing the indexes of the items
// e.g. path = [0, 0, 0] --> array[0][0][0]
function deleteItems(arr, path_index: number = 0, path: any[] = [0]): void {
// loop through arr
for (let i = 0; i < arr.length; i++) {
// get item
let item = arr[i];
// set path
path[path_index] = i;
// delete here somehow the item with corresponding path
if (item['exclude']) {
console.log('path', path);
}
// recursion
if ('content' in item) {
// +2 to path index (1. for content, 2. for i)
let path_index_ref = path_index + 2;
// create new path so old path does not get changed
let path_ref = path.slice();
path_ref.push('content');
this.deleteFlowchartItem(item['content'], path_index_ref, path_ref);
} // if content
} // for
} // deleteFlowchartItem()
Upvotes: 2
Views: 615
Reputation: 191976
Splicing items out of the array changes the length of the array, and makes iterating with for loops difficult. The usual solution is to iterate the arrays backwards, which makes the length irrelevant (0 index is the stop point).
However, I suggest a different approach, rebuild the entries tree recursively without the items you wish to exclude using Array#reduce. If the an object contains a sub array (according to predefined childrenProp
), a new object is created using Object#assign, and the sub array is add after filtering.
const array = [{"name":"A","exclude":true,"content":[{"name":"A-A"},{"name":"A-B","exclude":true},{"name":"A-C","content":[{"name":"A-C-A"}]}]},{"name":"A","content":[{"name":"A-A"},{"name":"A-B","exclude":true},{"name":"A-C","content":[{"name":"A-C-A"}]}]},{"name":"A","content":[{"name":"A-A"},{"name":"A-B","exclude":true},{"name":"A-C","content":[{"name":"A-C-A","exclude":true}]}]}];
const recursiveFilter = (arr, predicate, childrenProp) => arr.reduce((a, o) => {
// if predicate fails don't include the object and it's children
if(!predicate(o)) {
return a;
}
const obj = Array.isArray(o[childrenProp]) ? // if there is a sub array
Object.assign({}, o, { // create a new object from the original properties with the filtered sub array
[childrenProp]: recursiveFilter(o[childrenProp], predicate, childrenProp)
})
:
o; // or use the original object
a.push(obj);
return a;
}, []);
const result = recursiveFilter(array, ({ exclude }) => !exclude, 'content');
console.log(result);
Upvotes: 4
Reputation: 1
You can use a function to check if "exclude"
property is defined, if true delete
the element at that , else recursively iterate "content"
properties of objects, call delete
where "exclude"
is true
, use JSON.stringify()
, JSON.parse()
and String.prototype.replace()
to remove empty indexes cast to null
from array
let array = [{"name":"A","exclude":true,"content":[{"name":"A-A"},{"name":"A-B","exclude":true},{"name":"A-C","content":[{"name":"A-C-A"}]}]},{"name":"A","content":[{"name":"A-A"},{"name":"A-B","exclude":true},{"name":"A-C","content":[{"name":"A-C-A"}]}]},{"name":"A","content":[{"name":"A-A"},{"name":"A-B","exclude":true},{"name":"A-C","content":[{"name":"A-C-A","exclude":true}]}]}];
const fn = (o, arr, x = "exclude", it = "content") => {
if (o[x] === true) {
delete arr[arr.findIndex(obj => JSON.stringify(obj) === JSON.stringify(o))];
} else {
if (Array.isArray(o[it])) {
o.content.forEach(obj => fn(obj, o[it]))
}
}
}
array.forEach(o => fn(o, array));
array = JSON.parse(JSON.stringify(array).replace(/null,|null\b/g, ""));
console.log(array);
Upvotes: 0