How to filter nested objects? ReactJS

8.4k views Asked by At

Data sample:

nodes:[
        {
        label:"Egor1",
        value:"Egor1",
        restorePoint:"25/10/2017 10:00:29 PM",
        vmcount:"2",
        restorePointsCount:"",
        children:[
          {label:"disk111111111111111",
          value:"disk1",
          restorePoint:"3 days ago",
          vmcount:"",
          restorePointsCount:"11",
        },
        {label:"disk22222222222222",
        value:"disk2",
        name:"jobname2",
        restorePoint:"4 days ago",
        vmcount:"",
        restorePointsCount:"11"},
        {label:"disk555",
        value:"disk552",
        name:"jobnam555e2",
        restorePoint:"4 days ago",
        vmcount:"",
        restorePointsCount:"11"}
      ]}
      ,

      {
        label:"Egor12",
        value:"Egor12",
        restorePoint:"25/10/2017 10:00:29 PM",
        vmcount:"22",
        restorePointsCount:"",
        children:[
          {label:"disk111111111111111",
          value:"disk1",
          restorePoint:"2 days ago",
          vmcount:"",
          restorePointsCount:"12",
        },
        {label:"disk22222222222222",
        value:"disk2",
        name:"jobname2",
        restorePoint:"restorepoint4",
        vmcount:"",
        restorePointsCount:"12",}
      ]},


      ],

Try to do next:

filter(e) {
    var value = e.target.value;
    this.setState({filterval: value})
    this.setState({
      filteredItems: !value
        ? false
        : this.state.nodes.filter(function (item) {
          return 
          item.children.value.toLowerCase().indexOf(value.toLowerCase()) !== -1;
        })
    })
  }

But filter wants work, how filter by nested objects? Can't find any exmaple. it possible to do with _lodash?

For example I search label:"disk111111111111111

In result must be array like this :

{
        label:"Egor1",
        value:"Egor1",
        restorePoint:"25/10/2017 10:00:29 PM",
        vmcount:"2",
        restorePointsCount:"",
        children:[
          {label:"disk111111111111111",
          value:"disk1",
          restorePoint:"3 days ago",
          vmcount:"",
          restorePointsCount:"11",
        },

So it must return not only elements, that I search, it must return childrens with parents.

3

There are 3 answers

2
Hemerson Carlin On BEST ANSWER

You can mix map and filter. First one maps every item and filter its children (according your needs) and the second one filters items which has children.length > 0

const nodes = [
  {
    label: 'Egor1',
    value: 'Egor1',
    restorePoint: '25/10/2017 10:00:29 PM',
    vmcount: '2',
    restorePointsCount: '',
    children: [
      {
        label: 'disk111111111111111',
        value: 'disk1',
        restorePoint: '3 days ago',
        vmcount: '',
        restorePointsCount: '11',
      },
      {
        label: 'disk22222222222222',
        value: 'disk2',
        name: 'jobname2',
        restorePoint: '4 days ago',
        vmcount: '',
        restorePointsCount: '11',
      },
      {
        label: 'disk555',
        value: 'disk552',
        name: 'jobnam555e2',
        restorePoint: '4 days ago',
        vmcount: '',
        restorePointsCount: '11',
      },
    ],
  },
  {
    label: 'Egor12',
    value: 'Egor12',
    restorePoint: '25/10/2017 10:00:29 PM',
    vmcount: '22',
    restorePointsCount: '',
    children: [
      {
        label: 'disk111111111111111',
        value: 'disk1',
        restorePoint: '2 days ago',
        vmcount: '',
        restorePointsCount: '12',
      },
      {
        label: 'disk22222222222222',
        value: 'disk2',
        name: 'jobname2',
        restorePoint: 'restorepoint4',
        vmcount: '',
        restorePointsCount: '12',
      },
    ],
  },
]

const value = 'disk552'

const result = nodes
  .map(item => ({
    ...item,
    children: item.children
      .filter(child => child.value.includes(value.toLowerCase()))
  }))
  .filter(item => item.children.length > 0)
  
console.log(result)

0
synthet1c On

You could filter over the nodes then test the children.value to see which nodes to keep.

const FakeReact = {
  setState(newState) {
    this.state = Object.assign({}, this.state, newState)
  },
  state: {
    nodes: [
      { value: "Egor1", children: [{ value: "disk1" }, { value: "disk2" }] },
      { value: "Egor2", children: [{ value: "disk3" }, { value: "disk4" }] },
    ]
  }
}

const fakeEvent = { target: { value: 'disk1' }}

function filter(e) {
  const value = e.target.value;
  const regex = new RegExp('.*' + value + '.*', 'gi')
  this.setState({
    filterval: value, // may as well only make one call to set state
    filteredItems: (!value)
      ? [] // try to keep the same type for the false value
      : this.state.nodes.filter(
        // filter the node
        node =>
          node.children.find(
            // check if the child value matches the input value
            // if it does the node will be returned to filteredItems
            child => regex.test(child.value)
          )
        )
  })
}

// call filter with the FakeReact as the context and a fake event
filter.call(FakeReact, fakeEvent)

console.log('nodes', FakeReact.state.nodes)
console.log('filteredItems', FakeReact.state.filteredItems)
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js?theme=onedark"></script>

3
Vipin Kumar On

Your item.children is an array. If there is only one item in it, then you can do item.children[0]. Otherwise, you have to loop it too to check your condition.