user2298581
user2298581

Reputation: 682

Filter JSON data with recursive parent-child relationship

I have a JSON parent-child recursive structure made like in the example below.

How can I filter this JSON list to exclude just the object corresponding to a selected index?

 familyTree =[
  {
    name: "parent",
    index:"0",
    age:"50,
    children: [
         {
            name: "first child",
            index:"0.0",
            age:"30",
            children: [
                {
                    name: "first grandChild",
                    index:"0.0.0",
                    age:"5"
                },
                {
                    name: "second grandChild",
                    index: "0.0.1",
                    age:"2"
                }
            ]
         },
         {
            name: "second child",
            index:"0.1",
            age:"24",
         }
    ]
 }
]

E.g. If I want to exclude the element with index "0.0.1" ,my result will be:

 familyTree =[
   {
    name: "parent",
    index:"0",
    children: [
         {
            name: "first child",
            index:"0.0",
            age:"30",
            children: [
                {
                    name: "first grandChild",
                    index:"0.0.0",
                    age:"5"
                }
            ]
         },
         {
            name: "second child",
            index:"0.1",
            age:"24",
         }
    ]
 }
]

I've tried to use ES6 function filter() but it's probably the wrong use since it's not working

     const elemToRemove= familyTree[0].children[0].children[1]; //index: 0.0.1
     familyTree.filter(
        (elem) => JSON.stringify(elem) === JSON.stringify(elemToRemove)
     );

Thank you for your help

Upvotes: 0

Views: 1893

Answers (3)

pilchard
pilchard

Reputation: 12920

If you will always be accessing/removing elements by their index you can use it as an address to access the item and return it at the same time as removing it. This solution edits the original array in place.

const indexToRemove = '0.0.1';
const keys = indexToRemove.split('.').map((e, i, a) => a.slice(0, i+1).join('.'));
// ['0', '0.0', '0.0.1']

const removedItem = keys.reduce((a, k, i) => {
  if (i !== keys.length-1) {
    a = a.find(e => e.index === k).children;
  } else {
    const ri = a.map(e => e.index).indexOf(k);
    a = a.splice(ri, 1);
  }
  return a;
}, familyTree);

const familyTree =[
  {
    name: "parent",
    index:"0",
    age:"50",
    children: [
         {
            name: "first child",
            index:"0.0",
            age:"30",
            children: [
                {
                    name: "first grandChild",
                    index:"0.0.0",
                    age:"5"
                },
                {
                    name: "second grandChild",
                    index: "0.0.1",
                    age:"2"
                }
            ]
         },
         {
            name: "second child",
            index:"0.1",
            age:"24",
         }
    ]
 }
]

const indexToRemove = '0.0.1';
const keys = indexToRemove.split('.').map((e, i, a) => a.slice(0, i+1).join('.'));

const removedItem = keys.reduce((a, k, i) => {
  if (i !== keys.length-1) {
    a = a.find(e => e.index === k).children;
  } else {
    const ri = a.map(e => e.index).indexOf(k);
    a = a.splice(ri, 1);
  }
  return a;
}, familyTree);

console.log(removedItem);
console.log(familyTree);

Upvotes: 1

Mulan
Mulan

Reputation: 135277

Here's one possibe implementation of filterRec -

function filterRec (t, f)
{ const many = (t = []) =>
    t.flatMap(one)

  const one = (t = {}) =>
    Boolean(f(t))
      ? [ { ...t, children: many(t.children) } ]
      : []
  
  return many(t)
}

Usage is similar to Array.prototype.filter -

const result =
  filterRec(familyTree, elem => elem.index !== "0.0.1")
  
console.log(JSON.stringify(result, null, 2))
[
  {
    "name": "parent",
    "index": "0",
    "age": "50",
    "children": [
      {
        "name": "first child",
        "index": "0.0",
        "age": "30",
        "children": [
          {
            "name": "first grandChild",
            "index": "0.0.0",
            "age": "5",
            "children": []
          }
        ]
      },
      {
        "name": "second child",
        "index": "0.1",
        "age": "24",
        "children": []
      }
    ]
  }
]

Expand the snippet below to verify the results in your own browser -

function filterRec (t, f)
{ const many = (t = []) =>
    t.flatMap(one)

  const one = (t = {}) =>
    Boolean(f(t))
      ? [ { ...t, children: many(t.children) } ]
      : []
  
  return many(t)
}

const familyTree =
  [{name:"parent",index:"0",age:"50",children:[{name:"first child",index:"0.0",age:"30",children:[{name:"first grandChild",index:"0.0.0",age:"5"},{name:"second grandChild",index:"0.0.1",age:"2"}]},{name:"second child",index:"0.1",age:"24"}]}]
  
const result =
  filterRec(familyTree, elem => elem.index !== "0.0.1")
  
console.log(JSON.stringify(result, null, 2))

Upvotes: 1

richytong
richytong

Reputation: 2452

Here is a way to solve your problem. The function familyTreeFilterChildrenByIndex checks the index of each element and also runs itself on any children.

const familyTree =[ { name: "parent", index:"0", age:"50", children: [ { name: "first child", index:"0.0", age:"30", children: [ { name: "first grandChild", index:"0.0.0", age:"5" }, { name: "second grandChild", index: "0.0.1", age:"2" } ] }, { name: "second child", index:"0.1", age:"24", } ] } ]

const familyTreeFilterChildrenByIndex = function (familyTree, indexToRemove) {
  const result = []
  for (const parent of familyTree) {
    if (parent.index === indexToRemove) {
      continue
    } else if (parent.children) {
      result.push({
        ...parent,
        children: familyTreeFilterChildrenByIndex(parent.children, indexToRemove),
      })
    } else {
      result.push(parent)
    }
  }
  return result
}

console.log((familyTreeFilterChildrenByIndex(familyTree, '0.0.1')))

Upvotes: 1

Related Questions