Home > OS >  How do I filter a tree by a non-existing property in parent
How do I filter a tree by a non-existing property in parent

Time:09-03

I have a structure like this:

const tree = [
  {
    "a": "1",
    "children": [
      {
        "a": "11",
        "children": [
            { "a": "111", "p": "me" }, 
            { "a": "112" }
        ]
      },
      { "a": "111", "p": "me" }
    ]
  },
  { "a": "2" },
  { "text": "3", "p": "me" }
]

And I'm trying to filter the tree by p: "me", however, I keep bumping into the parent and can't get past it. My desired output is something in these lines:

const res = filter(tree, "p", "me")
console.log(res)
/*
[
  {
    "a": "1",
    "children": [
      {
        "a": "11",
        "children": [
            { "a": "111", "p": "me" }, 
        ]
      },
      { "a": "111", "p": "me" }
    ]
  },
  { "text": "3", "p": "me" }
]
*/

I've tried other solutions and tweak them (like this one), but I'm struggling

CodePudding user response:

Check my comments in code.

The trick is to know when to go into recursion to go deeper in the tree and when to return the true/false for the JS filter() method.

const tree = [
  {
    "a": "1",
    "children": [
      {
        "a": "11",
        "children": [
            { "a": "111", "p": "me" }, 
            { "a": "112" }
        ]
      },
      { "a": "111", "p": "me" }
    ]
  },
  { "a": "2" },
  { "text": "3", "p": "me" }
]

const filter = (array, property, value) => {
  // Filter an array...
  return array.filter((object) => {
    // If an object of the array has a 'children'
    if(object.children){
      // It goes into recursion to set the filtered 'children'
      object.children = filter(object.children, property, value)
      // Then returns true if the children array is not empty
      return object.children.length
    }
    // If the object has a 'p' === 'me' (passed as arguments), it returns true (or false to discards the object)
    return object[property] === value
  })
}

console.log(filter(tree, "p", "me"))

CodePudding user response:

We can build this atop a reusable deep-filtering function, just passing it a predicate to say whether we're keeping an object. This one uses a deepFilter function that I have lying around my virtual toolbox:

const deepFilter = (pred) => (xs) =>
  xs .flatMap (({children = [], ...rest}, _, __, kids = deepFilter (pred) (children)) =>
    pred (rest) || kids.length
      ? [{...rest, ...(kids.length ? {children: kids} : {})}]
      : []
  )

const keepProp = (name, value) =>
  deepFilter (x => x [name] == value)

const tree= [{a: "1", children: [{a: "11", children: [{a: "111", p:"me"}, {a: "112"}]}, {a: "111", p: "me"}]}, {a: "2"}, {text: "3", p: "me"}]

console .log (keepProp ('p', 'me') (tree))
.as-console-wrapper {max-height: 100% !important; top: 0}

Note that no objects are harmed in the use of this function. It treats your data as immutable.

  • Related