简体   繁体   中英

Filter nested array by matching keywords in both parent or child

I need to filter a nested array by matching a keyword. It must return all the objects that matches the name attribute of the parent OR one of its children and filter its children too.

Example:

[
  {
    "name": "abc",
    "children": [
      { "name": "abcd" },
      { "name": "efg" }
    ]
  },
  {
    "name": "hjk",
    "children": [
      { "name": "lmn" },
      { "name": "opq" }
    ]
  },
  {
    "name": "xyz",
    "children": [
      { "name": "lmn" },
      { "name": "abcdef" }
    ]
  }
]

If text input is "ab", then it must return:

[
  {
    "name": "abc",
    "children": [
      { "name": "abcd" }
    ]
  },
  {
    "name": "xyz",
    "children": [
      { "name": "abcdef" }
    ]
  }
]

(It matches the parent OR at least one of the children, and returns but parent and children)

Right now, I can only filter the parents like this:

filteredArray = _.filter(array, (parent) => {
    return _.includes(_.toLower(parent.name), _.toLower(filterText));
});

How can I modify this so it filters the children too?

EDIT: A parent can have empty or no children array.

IMHO, you can use Array#reduce() method to do something like this perhaps:

 arr = [{"name":"abc","children":[{"name":"abcd"},{"name":"efg"}]},{"name":"hjk","children":[{"name":"lmn"},{"name":"opq"}]},{"name":"xyz","children":[{"name":"lmn"},{"name":"abcdef"}]}, {"name": "xyz"}] var input = "ab" filteredArr = arr.reduce((accum, ele) => { var obj = {}; ele['children'] && ele['children'].forEach(e => { if (e['name'].includes(ele['name']) || e['name'].includes(input)) { obj['name'] = ele['name']; obj['children'] ? (obj['children'].push(e)) : (obj['children'] = [], obj['children'].push(e)) } }) if (Object.keys(obj).length > 0) accum.push(obj); return accum; }, []) console.log(filteredArr); 

For every element check name and filter its children and if one of them is true, push element with filtered children to a new array

 const data = [{"name": "abc", "children": [{ "name": "abcd" }, { "name": "efg" } ] }, {"name": "hjk","children": [{ "name": "lmn"}, { "name": "opq" } ]}, { "name": "xyz","children": [{ "name": "lmn" },{"name": "abcdef" } ] }, {"name": "abc"}, {"name": "abc", children: []}]; const res = data.reduce((acc, a) => { const ch = a.children && a.children.filter(b => b.name.includes('ab')); if(ch && ch.length) acc.push({...a, children: ch}); else if(a.name.includes('ab')) acc.push({ name: a.name }); return acc; }, []); console.log(res); 

There is no real need in lodash . #nodash! Just modern vanilla JS:

const regExp = new RegExp(filterText, 'i');

const result = array.reduce((acc, { name, children = [] }) => {
  const next = children.filter(child => child.name.match(regExp));

  if (name.match(regExp) || next.length > 0) {
    acc.push({ name, children: next });
  }

  return acc;
}, []);

name.match(regExp) will return null if substring is not found.

children = [] handles the case when parent has no children.

Also, note that checking for sub-string using RegExp should be much faster than performing multiple transformations and lookups (eg toLower , includes ).

Here is the working repl with your example .

var arrayList = [
  {
    "name": "abc",
    "children": [
      { "name": "abcd" },
      { "name": "efg" }
    ]
  },
  {
    "name": "ab",
    "children": [
      { "name": "lmn" },
      { "name": "opq" }
    ]
  },
  {
    "name": "jdfj",
    "children": [
      { "name": "lmn" },
      { "name": "abcdef" }
    ]
  }
];

function filterArray(arrayList, search){
    return arrayList.filter((item) => {
        let childrens = item.children;
        if(childrens && childrens.length){
          item.children = filterArray(childrens, search);
          if(item.children && item.children.length){
              return true;
          }
        }
        return item.name.indexOf(search) > -1;
    });
}

const filter = filterArray(arrayList, 'ab');

console.log(filter);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM