Search code examples
arraysjsontypescriptlodash

Finding a node with a given id in nested payload using lodash


I have the following nested JSON payload, which I'd like to iterate through, and find a matching id value, using lodash (I'm clearly still getting to grips with lodash). Below is an example of the payload, and the function I'm using - which seems a bit verbose. Is there an easier way to accomplish what I need?

JSON Payload:

{
  "_expanded": true,
  "_canDrop": false,
  "_id": "-1",
  "_name": "root",
  "_children": [
    {
      "_expanded": true,
      "_canDrop": false,
      "_id": "1",
      "_name": "Child 1",
      "_children": [
        {
          "_expanded": true,
          "_canDrop": false,
          "_id": "1-1",
          "_name": "Child 1-1",
          "_children": [
            {
              "_expanded": false,
              "_canDrop": false,
              "_id": "1-1-1",
              "_name": "Child 1-1-1",
              "_children": []
            }
          ]
        },
        {
          "_expanded": false,
          "_canDrop": false,
          "_id": "1-2",
          "_name": "Child 1-2",
          "_children": []
        },
        {
          "_expanded": false,
          "_canDrop": false,
          "_id": "1-3",
          "_name": "Child 1-3",
          "_children": []
        }
      ]
    },
    {
      "_expanded": true,
      "_canDrop": false,
      "_id": "2",
      "_name": "Child 2",
      "_children": [
        {
          "_expanded": false,
          "_canDrop": false,
          "_id": "2-2",
          "_name": "Child 2-2",
          "_children": []
        }
      ]
    }
  ]
}

Function:

  public findNode = (id: any): TreeNode => {
    let result = null;

    _.find(this._children, function(child) {
      if (child._id === id) {
        result = child;
      } else {
        if (child._children.length > 0) {
          _.find(child._children, function(item) {
            result = this.findNode(item._id);
          })
        }
      }
    });
    return result;
  }

Solution

  • Lodash doesn't have anything related to deep search, so you have to implement tree aspect of algorithm by yourself.

    Additionaly _.find that you've tried to use is used to find element that matches predicate, but doesn't allow you to change result. It can at best find only a root node he or one of his children has proper _id.

    So, this algorithm must be written manually:

    function findNode(id: any, node: any) {
        if (node._id === id) {
            return node;
        }
        for (const child of node._children) {
            const r = findNode(id, child);
            if (r !== undefined) {
                return r;
            }
        }
        return undefined;
    }
    

    If you really insist to be more 'functional', you can use reduce to replace for loop over children:

    return _.reduce(node._children, (result, child) => {
        if (result !== undefined) {
            return result;
        }
        return findNode(id, child);
    }, undefined)
    

    This basically iterates over children and checks each children unless something was already found