skip to Main Content

I am having some difficulties in what the best way to go about this is.

Example below:

const arr = [{
  parentId: 1,
  children: [
    { childId: 11 },
    { childId: 21 },
    { childId: 31 },
  ]
}, {
  parentId: 31,
  children: [
    { childId: 111 },
    { childId: 211 },
    { childId: 311 },
  ]
}, {
  parentId: 7,
  children: [
    { childId: 711 },
    { childId: 721 },
    { childId: 731 },
  ]
}, {
  parentId: 311,
  children: [
    { childId: 3111 },
    { childId: 3211 },
    { childId: 3311 },
  ]
}]

So, say I want to remove parentId = 1 then I would like to remove all its assoicated children and if the associated children also is a parentId then their subsequent children as well. Therefore, parentIds 11, 21, 31. 31 is a parentId so remove its childIds – 111, 211 and 311. 311 is a parentId so remove its childIds. No childIds as parentIds fr 311 so we can stop there.

Please if anyone is able to advise best way to go about it. I was thinking recursion but the examples I have seen is more for nested arrays rather than children then separate arrays and repeat the process.

So we would be left with

[{
  parentId: 7,
  children: [
    { childId: 711 },
    { childId: 721 },
    { childId: 731 },
  ]
}]

5

Answers


  1. One possibility is to have a queue of ids-to-delete, delete an id on the top of the queue and append all dependent ids. For convenience, let’s also convert your array to a map id->object:

    const arr=[{parentId:1,children:[{childId:11},{childId:21},{childId:31}]},{parentId:31,children:[{childId:111},{childId:211},{childId:311}]},{parentId:7,children:[{childId:711},{childId:721},{childId:731}]},{parentId:311,children:[{childId:3111},{childId:3211},{childId:3311}]}]
    
    //
    
    let m = new Map(arr.map(o => [o.parentId, o]))
    let remove = [1]
    
    while (remove.length > 0) {
        let id = remove.shift()
        let obj = m.get(id)
        if (obj) {
            console.log('debug: deleting', id)
            m.delete(id)
    
            remove.push(...obj.children.map(c => c.childId))
        }
    }
    
    let result = [...m.values()]
    console.log(result)
    Login or Signup to reply.
  2. You need no recursion, because of the sorted and flat data and you could filter with a closure over a Set where all childId are collected.

    const
        remove = (data, id) => data.filter((s => o => {
            if (!s.has(o.parentId)) return true;
            o.children.forEach(({ childId }) => s.add(childId));
        })(new Set([id]))),
        data = [{ parentId: 1, children: [{ childId: 11 }, { childId: 21 }, { childId: 31 }] }, { parentId: 31, children: [{ childId: 111 }, { childId: 211 }, { childId: 311 }] }, { parentId: 7, children: [{ childId: 711 }, { childId: 721 }, { childId: 731 }] }, { parentId: 311, children: [{  childId: 3111 }, { childId: 3211 }, { childId: 3311 }] }],
        result = remove(data, 1);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }
    Login or Signup to reply.
  3. You can use recursion to solve this.

    const arr=[{parentId:1,children:[{childId:11},{childId:21},{childId:31}]},{parentId:31,children:[{childId:111},{childId:211},{childId:311}]},{parentId:7,children:[{childId:711},{childId:721},{childId:731}]},{parentId:311,children:[{childId:3111},{childId:3211},{childId:3311}]}];
    
    function myReduce(inputArr, key) {
      for (let [i, arrElem] of inputArr.entries()) {
        if (arrElem.parentId && arrElem.parentId === key) {
          const childElem = arrElem.children;
          if (childElem.length) {
            for (let child of childElem) {
              myReduce(inputArr, child.childId);
            }
          }
          inputArr.splice(i, 1);
        }
      }
      return inputArr;
    }
    
    const newArr = myReduce(arr, 1);
    document.getElementById('output').innerHTML = JSON.stringify(newArr, null, 2);
    <pre id="output"></pre>
    Login or Signup to reply.
  4. A possibility using recursion and the Ramda.js library:

    import * as R from 'ramda'
    
    const initialArr = [
      { parentId: 1, children: [{ childId: 11 }, { childId: 21 }, { childId: 31 }] }, 
      { parentId: 31, children: [{ childId: 111 }, { childId: 211 }, { childId: 311 }] }, 
      { parentId: 7, children: [{ childId: 711 }, { childId: 721 }, { childId: 731 }] }, 
      { parentId: 311, children: [{ childId: 3111 }, { childId: 3211 }, { childId: 3311 }] }
    ]
    
    const removeDesc = (id,arr) => R.pipe(
      R.find(R.propEq("parentId",id)),            // find parent
      R.propOr([],"children"),                    // handle zero case  
      R.pluck("childId"),                         // find the children  
      R.reduce(R.flip(removeDesc), arr),          // recurse the children
      R.reject(R.propEq("parentId", id))          // then remove the parent
    )(arr)
    
    console.log( JSON.stringify( removeDesc(1,initialArr) ) )   
    // [{"parentId":7,"children":[{"childId":711},{"childId":721},{"childId":731}]}]
    

    Using a functional-programming style like this I find makes the code easier to follow (in the long-run!). It tends to encourage using immutable data structures too, so there’s less risk of unintended consequences through modification. It’s not necessarily the most efficient at run-time though.

    Login or Signup to reply.
  5. You can use this approach with lodash and recursion:

    const arr = [{
      parentId: 1,
      children: [
        { childId: 11 },
        { childId: 21 },
        { childId: 31 },
      ]
    }, {
      parentId: 31,
      children: [
        { childId: 111 },
        { childId: 211 },
        { childId: 311 },
      ]
    }, {
      parentId: 7,
      children: [
        { childId: 711 },
        { childId: 721 },
        { childId: 731 },
      ]
    }, {
      parentId: 311,
      children: [
        { childId: 3111 },
        { childId: 3211 },
        { childId: 3311 },
      ]
    }]
    
    const removeByParentId = (arr, parentId) => {
      const parent = _.find(arr, { parentId });
      if (!parent) return arr;
      
      let updatedArr = _.reject(arr, { parentId });
      parent.children.forEach(child => {
        updatedArr = removeByParentId(updatedArr, child.childId);
      });
    
      return updatedArr;
    };
    
    const outputArr = removeByParentId(arr, 1);
    
    console.log(outputArr);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search