Search code examples
javascriptarraysobjectwhile-loopnested-loops

Javascript flatten deep nested children


I really can't figure out this one. I'm trying to flatten the category_id that are deep child of a specific node.

var categories = [{
  "category_id": "66",
  "parent_id": "59"
}, {
  "category_id": "68",
  "parent_id": "67",
}, {
  "category_id": "69",
  "parent_id": "59"
}, {
  "category_id": "59",
  "parent_id": "0",
}, {
  "category_id": "67",
  "parent_id": "66"
}, {
  "category_id": "69",
  "parent_id": "59"
}];

Or visually:

enter image description here

The closest I got to was to recursively loop through the first item found:

function children(category) {
    var children = [];
    var getChild = function(curr_id) {
        // how can I handle all of the cats, and not only the first one?
        return _.first(_.filter(categories, {
            'parent_id': String(curr_id)
        }));
    };

    var curr = category.category_id;

    while (getChild(curr)) {
        var child = getChild(curr).category_id;
        children.push(child);
        curr = child;
    }

    return children;
}

Current output of children(59) is ['66', '67', '68'].

Expected output is ['66', '67', '68', '69']


Solution

  • I didn't test but it should work:

    function getChildren(id, categories) {
      var children = [];
      _.filter(categories, function(c) {
        return c["parent_id"] === id;
      }).forEach(function(c) {
        children.push(c);
        children = children.concat(getChildren(c.category_id, categories));
      })
    
      return children;
    }
    

    I am using lodash.

    Edit: I tested it and now it should work. See the plunker: https://plnkr.co/edit/pmENXRl0yoNnTczfbEnT?p=preview

    Here is a small optimisation you can make by discarding the filtered categories.

    function getChildren(id, categories) {
      var children = [];
      var notMatching = [];
      _.filter(categories, function(c) {
        if(c["parent_id"] === id)
          return true;
        else
          notMatching.push(c);
      }).forEach(function(c) {
        children.push(c);
        children = children.concat(getChildren(c.category_id, notMatching));
      })
    
      return children;
    }