2

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()

2 Answers 2

4

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);

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! Some of the stuff is very new to me but I understand it slowly. But I get the TypeScript error Initializer provides no value for this binding element and the binding element has no default value.. How can I get rid of it?
Try change the const result... line to const result = recursiveFilter(array, ({ exclude }) => !exclude, 'content');
0

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.