0

How do I filter nested objects in React? Currently my function is only looking for the main title, not the nested ones.

export const treeData = [
  {
    title: 'Orange',
    key: '0-0',
    children: [
      {
        title: 'Anu',
        key: '0-0-0',
        isLeaf: true,
      },
      {
        title: 'Anurag',
        key: '0-0-1',
        isLeaf: true,
      },
    ],
  },
  {
    title: 'ABC',
    key: '0-1',
    children: [
      {
        title: 'leaf 1-0',
        key: '0-1-0',
        isLeaf: true,
      },
      {
        title: 'leaf 1-1',
        key: '0-1-1',
        isLeaf: true,
      },
    ],
  },
]

export default {
  treeData,
}

and the function is:

 const filtered = treeData.filter((data) => {
      return data.title.toLowerCase().includes(e.target.value.toLowerCase())
    })

So currently it is only searching the Orange and ABC, not the titles from children.

2
  • What does 'React' have to do with it? Commented Sep 8, 2021 at 15:32
  • What format do you need to get the data in? Or what are you looking for? Commented Sep 8, 2021 at 15:32

3 Answers 3

2

I recommend flattening the title values and then filtering. Note, the end result will contain a mixture of top level "parent" data and children.

const flattened = treeData.reduce((acc, data) => {
  acc.push(data, ...data.children)
  return acc;
}, []);

const filtered = flattened.filter((data) => {
  return data.title.toLowerCase().includes(e.target.value.toLowerCase())
})

If you only need a filtered list of top level data, you will need to do something like the following:

function dataIncludesTitle(data, title) {
  return data.title.toLowerCase().includes(title);
}

const searchValue = e.target.value.toLowerCase();
const filtered = treeData.filter((data) => {
  return dataIncludesTitle(data, searchValue) || data.children.some(child => dataIncludesTitle(child, searchValue));
})

UPDATE (from comments):

If you want to use the second solution, but you also want to only keep the children which contain the search value, you will need to do an additional map where you filter the children:

const filtered = treeData.filter((data) => {
  return dataIncludesTitle(data, searchValue) || data.children.some(child => dataIncludesTitle(child, searchValue));
}).map((data) => {
  return {
    ...data,
    children: data.children.filter((child) => dataIncludesTitle(child, searchValue))
  };
});
Sign up to request clarification or add additional context in comments.

2 Comments

Really great solution! Is there a way for the last solution (getting the top level data), Is there a way to get the top level data and only the filtered list of children because right now implementing your solution (the last one), I get all the children instead of just those that match the title.
I updated the answer as it was too much to type in a comment.
1

Something like:

const filtered = treeData.filter((data) => {
  return data.title.toLowerCase().includes(e.target.value.toLowerCase()) ||
         data.children.filter(x => x.title.toLowerCase().includes(e.target.value.toLowerCase())).length > 0
})

Look into data.children filtering subobject's title that contains e.target.value.toLowerCase().

2 Comments

worked like a charm! cannot believe it's just one line hahah I spent an hour. Thank you
@WildThing No problem. Happy coding =)
1

If you want to get full objects containing child objects with a title similar to the search string, try this:

const treeData = [
  {
    title: 'Orange',
    key: '0-0',
    children: [
      {
        title: 'Anu',
        key: '0-0-0',
        isLeaf: true,
      },
      {
        title: 'Anurag',
        key: '0-0-1',
        isLeaf: true,
      },
    ],
  },
  {
    title: 'ABC',
    key: '0-1',
    children: [
      {
        title: 'leaf 1-0',
        key: '0-1-0',
        isLeaf: true,
      },
      {
        title: 'leaf 1-1',
        key: '0-1-1',
        isLeaf: true,
      },
    ],
  },
];

const searchString = 'ANUR';

const filtered = treeData.filter((datum) => {
  const filteredChilds = datum.children.filter((child) => child.title.toLowerCase().includes(searchString.toLowerCase()));
  return filteredChilds.length > 0;
});

console.log(filtered);

If you want to get these objects and only matched child objects try this:

const treeData = [{
    title: 'Orange',
    key: '0-0',
    children: [{
        title: 'Anu',
        key: '0-0-0',
        isLeaf: true,
      },
      {
        title: 'Anurag',
        key: '0-0-1',
        isLeaf: true,
      },
    ],
  },
  {
    title: 'ABC',
    key: '0-1',
    children: [{
        title: 'leaf 1-0',
        key: '0-1-0',
        isLeaf: true,
      },
      {
        title: 'leaf 1-1',
        key: '0-1-1',
        isLeaf: true,
      },
    ],
  },
];

const searchString = 'ANUR';

const filtered = treeData.map((datum) => {
  const filteredChilds = datum.children.filter((child) => child.title.toLowerCase().includes(searchString.toLowerCase()));
  return {
    ...datum,
    children: filteredChilds
  };
}).filter(datum => datum.children.length > 0);

console.log(filtered);

If you want to get only matched child objects try this:

const treeData = [{
        title: 'Orange',
        key: '0-0',
        children: [{
            title: 'Anu',
            key: '0-0-0',
            isLeaf: true,
          },
          {
            title: 'Anurag',
            key: '0-0-1',
            isLeaf: true,
          },
        ],
      },
      {
        title: 'ABC',
        key: '0-1',
        children: [{
            title: 'leaf 1-0',
            key: '0-1-0',
            isLeaf: true,
          },
          {
            title: 'leaf 1-1',
            key: '0-1-1',
            isLeaf: true,
          },
        ],
      },
    ];

    const searchString = 'ANUR';

    const filtered = treeData.reduce((acc, datum) => acc.concat(datum.children.filter((child) => child.title.toLowerCase().includes(searchString.toLowerCase()))), []);

    console.log(filtered);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.