Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build a JSON object from absolute filepaths

I receive (in my angularjs application) from a server a list of directories like this:

['.trash-user',
 'cats',
 'cats/css',
 'cats/images/blog',
 'cats/images/gallery']

And I would like to build a javascript variable which looks like this:

[{
 label: '.trash-user'},
{label: 'cats',
 children: [{
   label: 'css'},
  {label: 'images',
   children: [{
      label: 'blog'},
     {label: 'gallery'}
     ]}
  ]}
}]

The paths are in random order.

Hope somebody has some really elegant solution, but any solution is appreciated!

Edit: Here is my naive approach, I have real trouble with recursion. I could only make level 0 to work:

var generateTree = function(filetree){
  console.log('--------- filetree -------');
  var model = [];
  var paths = [];
  for(var i=0;i<filetree.length;i++) {
    paths = filetree[i].split('/');
    for(var j=0;j<paths.length;++j) {
      var property = false;
      for(var k=0;k<model.length;++k) {
        if (model[k].hasOwnProperty('label') &&
            model[k].label === paths[0]) {
          property = true;
        }
      }
      if (!property) {
        model.push({label: paths[0]});
      }
    }
  }
  console.log(model);
};
like image 663
arcol Avatar asked Mar 24 '15 18:03

arcol


1 Answers

If you want an elegant solution, lets start with a more elegant output:

{
  '.trash-user': {},
  'cats': {
    'css': {},
    'images': {
      'blog': {},
      'gallery': {},
    },
  },
}

Objects are much better than arrays for storing unique keys and much faster too (order 1 instead of order n). To get the above output, do:

var obj = {};
src.forEach(p => p.split('/').reduce((o,name) => o[name] = o[name] || {}, obj));

or in pre-ES6 JavaScript:

var obj = {};
src.forEach(function(p) {
  return p.split('/').reduce(function(o,name) {
    return o[name] = o[name] || {};
  }, obj);
});

Now you have a natural object tree which can easily be mapped to anything you want. For your desired output, do:

var convert = obj => Object.keys(obj).map(key => Object.keys(obj[key]).length?
  { label: key, children: convert(obj[key]) } : { label: key });
var arr = convert(obj);

or in pre-ES6 JavaScript:

function convert(obj) {
  return Object.keys(obj).map(function(key) {
    return Object.keys(obj[key]).length?
      { label: key, children: convert(obj[key])} : { label: key };
  });
}
var arr = convert(obj);

I'll venture that generating the natural tree first and then converting to the array will scale better than any algorithm working on arrays directly, because of the faster look-up and the natural impedance match between objects and file trees.

JSFiddles: ES6 (e.g. Firefox), non-ES6.

like image 198
jib Avatar answered Sep 20 '22 16:09

jib