javascript - Build a JSON object from absolute filepaths - Stack Overflow

I receive (in my angularjs application) from a server a list of directories like this:['.trash-use

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);
};

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);
};
Share Improve this question edited Mar 24, 2015 at 19:33 arcol asked Mar 24, 2015 at 18:29 arcolarcol 1,6781 gold badge16 silver badges21 bronze badges 5
  • 1 "could not get it to work" is not an adequate explanatory statement. – Dai Commented Mar 24, 2015 at 18:31
  • 1 This sounds like something you should be doing on the server side, not on the Angular side. – David says Reinstate Monica Commented Mar 24, 2015 at 18:31
  • @DavidGrinberg: The same question is valid for the server side too. Let's imagine a node.js application. Game is on:) – arcol Commented Mar 24, 2015 at 18:47
  • @Dai: I do not want influence people with my bad approach. And it is not working. I will post eventually my non-working solution in the ing days, if no solution pops up... – arcol Commented Mar 24, 2015 at 18:48
  • @arcol Post it now. The solution will almost definitely need recursion – David says Reinstate Monica Commented Mar 24, 2015 at 19:01
Add a ment  | 

2 Answers 2

Reset to default 6

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.

Something like this should work:

function pathsToObject(paths) {
    var result = [ ];

    // Iterate through the original list, spliting up each path
    // and passing it to our recursive processing function
    paths.forEach(function(path) {
        path = path.split('/');
        buildFromSegments(result, path);
    });

    return result;

    // Processes each path recursively, one segment at a time
    function buildFromSegments(scope, pathSegments) {
        // Remove the first segment from the path
        var current = pathSegments.shift();

        // See if that segment already exists in the current scope
        var found = findInScope(scope, current);

        // If we did not find a match, create the new object for
        // this path segment
        if (! found) {
            scope.push(found = {
                label: current
            });
        }

        // If there are still path segments left, we need to create
        // a children array (if we haven't already) and recurse further
        if (pathSegments.length) {
            found.children = found.children || [ ];
            buildFromSegments(found.children, pathSegments);
        }
    }

    // Attempts to find a ptah segment in the current scope
    function findInScope(scope, find) {
        for (var i = 0; i < scope.length; i++) {
            if (scope[i].label === find) {
                return scope[i];
            }
        }
    }
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744182856a4562064.html

相关推荐

  • javascript - Build a JSON object from absolute filepaths - Stack Overflow

    I receive (in my angularjs application) from a server a list of directories like this:['.trash-use

    9天前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信