Constantine Golub Constantine Golub - 1 month ago 13
Javascript Question

Lodash re-assign array of objects to build a tree

Is there a short way to re-assign array that consists of objects into a new one. I want to use Lodash library and can't figure out how to split it inside and concatenate. I need this to build a tree using jstree library

I have something like this:

[
{
'text':'parent 1',
'children': [
{
'text':'children 3'
}
]
},
{
'text':'parent 2',
'children': []
},
{
'text':'parent 3',
'children': []
},
{
'text':'parent 1',
'children': [
{
'text':'children 1',
'children': []
},
{
'text':'children 2',
'children': []
}
]
}
]


And I want to have the next after transforming

[
{
'text':'parent 2',
'children': []
},
{
'text':'parent 3',
'children': []
},
{
'text':'parent 1',
'children': [
{
'text':'children 1',
'children': []
},
{
'text':'children 2',
'children': []
},
{
'text':'children 3'
}
]
}
]


And I want to combine all objects if 'text' is the same and concatenate their children.

The nested level may be as much as possible, no restriction.

[
{
'text':'parent 1',
'children': [
{
'text':'children 2',
'children': [
{
'text':'parent 3',
'children': [
{
'text':'parent 4',
'children': [
...
]
},
]
},
]
}
]
}
]


Is it possible?

Answer

If you just want to combine objects with the same 'text' and remove duplicates, one solution using lodash could be:

var d = [{
  'text': 'parent 1',
  'children': []
}, {
  'text': 'parent 2',
  'children': []
}, {
  'text': 'parent 3',
  'children': []
}, {
  'text': 'parent 1',
  'children': [{
    'text': 'children 1',
    'children': []
  }, {
    'text': 'children 2',
    'children': []
  }]
}];

var res = _.reduce(d, (memo, o) => {
  var already = _.find(memo, o1 => o.text === o1.text);
  return already ? (already.children = already.children.concat(o.children), memo) : memo.concat(o);
}, []);
console.log(res);
<script src="https://cdn.jsdelivr.net/lodash/4.16.4/lodash.min.js"></script>

This solution doesn't cover building a tree of more than two levels though.

UPDATE

Supposing the structure of your array would represent that of a tree: each "level" represents a level of the tree and objects in the same level in the array are in the same level in the array, a possible solution using recursion could be:

var tree = [{
  'text': 'A',
  'children': [{
    'text': 'A.1',
    'children': [{
      'text': 'A.1.1',
      'children': [{
        'text': 'A.1.1.1',
        'children': [{
          'text': 'A.1.1.1.1'
        }]
      }, {
        'text': 'A.1.1.1',
        'children': [{
          'text': 'A.1.1.1.2'
        }]
      }]
    }]
  }, {
    'text': 'A.2',
    'children': [{
      'text': 'A.2.1',
      'children': [{
        'text': 'A.2.1.1',
        'children': []
      }]
    }]
  }]
}, {
  'text': 'B',
  'children': []
}, {
  'text': 'C',
  'children': []
}, {
  'text': 'A',
  'children': [{
    'text': 'A.3',
    'children': []
  }, {
    'text': 'A.4',
    'children': []
  }]
}];

function joinChildren(childrenArray) {
  return _.reduce(childrenArray, function(memo, o) {
    var already = _.find(memo, function(o1) {
      return o.text === o1.text;
    });
    return already ? (already.children = already.children.concat(o.children), memo) : memo.concat(o);
  }, []);
}

function joinChildrenRecursively(rootArray) {
  rootArray = joinChildren(rootArray);
  _.forEach(rootArray, obj => {
    if (_.isArray(obj.children)) {
      obj.children = joinChildrenRecursively(obj.children);
    }
  });
  return rootArray;
}

console.log(joinChildrenRecursively(tree));
<script src="https://cdn.jsdelivr.net/lodash/4.16.4/lodash.min.js"></script>

Hope it helps.