Hoolagon
Hoolagon

Reputation: 248

How to create a nested filter with lodash

I have the following nested JSON data structure. Each node can have any number of children and the data can be any number of nodes deep.

[{
    id : "a",
    path : "a"
}, {
    id : "b",
    path : "b"
}, {
    id : "c",
    path : "c",
    children: [{
        id : "a",
        path : "c/a"
    }, {
        id : "b",
        path : "c/b",
        children: [{
            id : "a",
            path : "c/b/a"
        }, {
            id : "b",
            path : "c/b/b"
        }]
    }]
}]

I need to create a function in lodash (v3.10.1) which returns a nested JSON object of matching paths, and any parent objects. For example, if I was to search on "b" the filter should return the following:

[{
    id : "b",
    path : "b"
}, {
    id : "c",
    path : "c",
    children: [{
        id : "b",
        path : "c/b",
        children: [{
            id : "a",
            path : "c/b/a"
        }, {
            id : "b",
            path : "c/b/b"
        }]
    }]
}]

My initial attempt was like this but it did work:

const filterTree = (filter, list) => {
    return _.filter(list, (item) => {
        if (item.path) {
            return _.includes(item.path.toLowerCase(), filter.toLowerCase());
        } else if (item.children) {
            return !_.isEmpty(filterTree(filter, item.children));
        }
    });
};

Any help would be much appreciated

Upvotes: 1

Views: 3356

Answers (1)

castletheperson
castletheperson

Reputation: 33486

The first issue is that if (item.path) is always true, so the recursive calls never happen.

In order to get your desired result, you will have to update item.children after filtering in the recursive cases, because _.filter will not mutate the array that you pass to it. If you don't want the input to be mutated, use _.cloneDeep to make a copy first.

const data = [{"id":"a","path":"a"},{"id":"b","path":"b"},{"id":"c","path":"c","children":[{"id":"a","path":"c/a"},{"id":"b","path":"c/b","children":[{"id":"a","path":"c/b/a"},{"id":"b","path":"c/b/b"}]}]}];

const filterTree = (filter, list) => {
  return _.filter(list, (item) => {
    if (_.includes(_.toLower(item.path), _.toLower(filter))) {
      return true;
    } else if (item.children) {
      item.children = filterTree(filter, item.children);
      return !_.isEmpty(item.children);
    }
  });
};

console.log(filterTree('b', data));
.as-console-wrapper { max-height: 100% !important; }
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

Upvotes: 2

Related Questions