0

I want to be able to remove an object from an array of objects that have children. I thought immediately this is a job for recursion, but I am unable to get my recursion function to work properly. I thought of using reduce to rebuild the data structure without the object that I want to remove. The function should accept two parameters the array of nested objects and an Id.

My requirements are: remove the node and all children below.

At first glance, this seems easy, but the challenge I find is removing a child and keeping the entire data structure intact. Deleting the parent by filtering based on the id is trivial, but the nested children pose a problem.

My data structure looks like this:

const data = [{
  id: 'BFQEA1W2RK1YRETZ9343',
  name: 'Cover',
  activityId: 'BFQEA1W2RK1YRETZ9343',
  nodeType: 'activity',
  suppressed: true,
  hidden: true
},
{
  children: [
    {
      id: 'ZNRAE749BSD0CTGHY888',
      name: 'Consultants, Reviewers, and National Geographic Exploration',
      activityId: 'ZNRAE749BSD0CTGHY888',
      nodeType: 'activity',
      suppressed: false,
      hidden: false
    },
    {
      id: 'JZLS37EVZQM22H9Q4655',
      name: 'The National Geographic Approach',
      activityId: 'JZLS37EVZQM22H9Q4655',
      nodeType: 'activity',
      suppressed: false,
      hidden: false
    },
  ]
}
]

If I pass this Id(ZNRAE749BSD0CTGHY888) to the function my expected data result should be:

const expected =  [{
  id: 'BFQEA1W2RK1YRETZ9343',
  name: 'Cover',
  activityId: 'BFQEA1W2RK1YRETZ9343',
  nodeType: 'activity',
  suppressed: true,
  hidden: true
},
{
  children: [

    {
      id: 'JZLS37EVZQM22H9Q4655',
      name: 'The National Geographic Approach',
      activityId: 'JZLS37EVZQM22H9Q4655',
      nodeType: 'activity',
      suppressed: false,
      hidden: false
    },
  ]
}
]

My function looks like this:

 findNode = (id, arr) => {
    return arr.reduce((a, item) => {
      // if (item.id === id) {
      //   console.log('here');
      //   return item;
      // }
      if (item.id !== id) {
        a.push(item);
      }
      if (item.children) {
        return this.findNode(id, item.children);
      }
    }, []);
  };

The function's reduce accumulator is undefined, but I am unsure why. It should be making a new array. What am I missing here?

In my head, this seems to work, but it fails. Maybe my approach is completely off. How should I go about solving this?

Louis345
  • 700
  • 2
  • 11
  • 38
  • 1
    Do your want to remove the node or make a new array with the node filtered out – Mark Dec 30 '18 at 17:43
  • make a new array with the node filtered out. – Louis345 Dec 30 '18 at 17:55
  • See https://stackoverflow.com/questions/38132146/recursively-filter-array-of-objects – danh Dec 30 '18 at 18:03
  • @danh I will take a look. – Louis345 Dec 30 '18 at 18:04
  • Looking at this a little closer…none of the objects that have `id` properties have children. So it's not clear what you are trying to filter when you say `remove the node and all children below`. – Mark Dec 30 '18 at 18:11
  • Each child should have an idea that I need to filter. If the parent has an Id I should be able to remove that and if the parent has a child I need to look inside the children and find the Id and remove that. If the parent has children and the Id matches I should remove the children also. Does that make sense? – Louis345 Dec 30 '18 at 18:16
  • @NinaScholz One second will do. – Louis345 Dec 30 '18 at 18:25
  • If I understand your question correctly, some objects have children and others do not. In the data model I posted, the parent has children so I would have to search the parent or the children to locate the id and remove it. Sometimes the Id might be on the parent which is easy, but if the id is nested in the children it becomes complicated for me. – Louis345 Dec 30 '18 at 18:44

1 Answers1

2

In your code you are not returning the accumulator. That's why you're getting undefined. And there's no reason to recurse over children of items that you don't push, so you should nest the recursion under the if.

You can loop over you root array with reduce(). If the id matches, just return and continue. Other wise you can recursively pass the children the filter and push to the return array:

const data = [{id: 'BFQEA1W2RK1YRETZ9343',name: 'Cover',activityId: 'BFQEA1W2RK1YRETZ9343',nodeType: 'activity',suppressed: true,hidden: true},{children: [{id: 'ZNRAE749BSD0CTGHY888',name: 'Consultants, Reviewers, and National Geographic Exploration',activityId: 'ZNRAE749BSD0CTGHY888',nodeType: 'activity',suppressed: false,hidden: false},{id: 'JZLS37EVZQM22H9Q4655',name: 'The National Geographic Approach',activityId: 'JZLS37EVZQM22H9Q4655',nodeType: 'activity',suppressed: false,hidden: false},]}]


function filterID(id, data) {
  return data.reduce((arr, item) => {
    if (item.id != id) {
      if (item.children) item.children = filterID(id, item.children)
      arr.push(item)
    }
    return arr  // <<-- need to return the accumulator  
  }, [])
}
console.log(filterID("ZNRAE749BSD0CTGHY888", data))
Mark
  • 90,562
  • 7
  • 108
  • 148