0

I have a flat list of directories:

var directories = [
    { path: '/a', status: 0 },
    { path: '/a/a', status: 1 },
    { path: '/a/b', status: 1 },
    { path: '/a/c', status: 0 },
    { path: '/b', status: 0 },
    { path: '/b/a', status: 0 },
    { path: '/b/b', status: 1 },
    { path: '/c', status: 0 },
    { path: '/c/a', status: 2 },
    { path: '/c/b', status: 0 },
    { path: '/c/c', status: 1 },
    { path: '/c/d', status: 1 },
    { path: '/d', status: 0 },
    { path: '/d/a', status: 1 },
    { path: '/e', status: 1 },
    { path: '/e/a', status: 0 }
];

I would like to have the total children of each directory and the totals of those childrens by their status number e.g.

{ path: '/a', status: 0, child_total: 3, child_status-0: 1, child_status-1: 2 }

I believe the best way to do this is to create an Object containing named paths, then loop through each parent of a directory and add one to each total.

Using this approach I've created these functions:

function increment(item, attr) {
    if (!item[attr]) { item[attr] = 0; }
    item[attr] += 1;
};

function calculate(items) {
    var i = 0;
    var list = {};
    // for each directory
    for (i = 0; i < items.length; i += 1) {
        // if the directory stats already exist, merge in directory info
        list[items[i].path] = list[items[i].path] ? Object.assign(list[items[i].path], items[i]) : items[i];
        var j = 0;
        var parts = items[i].path.split('/');
        var path = '';
        // loop through it's parents
        for (j = 1; j < parts.length; j += 1) {
            path += '/' + parts[j];
            // increment the parent's totals
            if (path !== items[i].path) {
                if (!list[path]) { list[path] = {}; }
                increment(list[path], 'child_total');
                increment(list[path], 'child_status-' + items[i].status);
            }
        }
    }
    return list;
}

Here is a working fiddle:

https://jsfiddle.net/kmturley/b955d3mb/1/

This is the directory browser I'm looking to use the structure on:

https://jsfiddle.net/kmturley/x93pcd60/

I'm planning to run this function on thousands of items so performance is very important. I'm wondering if this is the best approach?

Is there a better way? without a loop within a loop? Maybe a RegEx for splitting the paths?

Any suggestions would help!

Kim T
  • 5,770
  • 1
  • 52
  • 79
  • Why is `child_total` for `path:"/a"` `3`? – guest271314 Dec 23 '16 at 04:23
  • are you need only for top-level parent paths to be processed? – Azad Dec 23 '16 at 04:25
  • /a has 3 children /a/a, /a/b and /a/c. I will need all levels processed eventually, but only one level is shown at a time e.g. nested directory structure – Kim T Dec 23 '16 at 04:29
  • Why is `status` for `path:"/a"` `0`? – guest271314 Dec 23 '16 at 04:29
  • each directory has it's own status e.g. 0 = 'added', 1 = 'deleted', 2 = 'ignored' – Kim T Dec 23 '16 at 04:36
  • @KimT Why does the resulting object have a `status` set to `0` though the `status` for each object which has a path ending in `"/a"`, that is `"/a/a"`, `"/a/b"`, `"/a/c"`, is `1`, `1`, `0`, respectively? – guest271314 Dec 23 '16 at 04:38
  • sub-directories may have status of added/deleted = 1/2, but the parent hasn't been changed itself. It's status is independent of it's children. – Kim T Dec 23 '16 at 04:46

1 Answers1

0

Is there a better way? without a loop within a loop? Maybe a RegEx for splitting the paths?

Not sure how to determine what "a better way" is. Only you can make such a determination.

You can use Array.prototype.reduce() to iterate each object in original array, accrue data about the object at an object.

Substituted an array of statuses where property is the current path and status is the status for the current object, for child_status-0: 1, child_status-N: 2 properties.

var directories = [
    { path: '/a', status: 0 },
    { path: '/a/a', status: 1 },
    { path: '/a/b', status: 1 },
    { path: '/a/c', status: 0 },
    { path: '/b', status: 0 },
    { path: '/b/a', status: 0 },
    { path: '/b/b', status: 1 },
    { path: '/c', status: 0 },
    { path: '/c/a', status: 2 },
    { path: '/c/b', status: 0 },
    { path: '/c/c', status: 1 },
    { path: '/c/d', status: 1 },
    { path: '/d', status: 0 },
    { path: '/d/a', status: 1 },
    { path: '/e', status: 1 },
    { path: '/e/a', status: 0 }
];

let pathData = directories.reduce((paths, {path, status}) => {

  let dir = new URL("http://mock" + path);
  let pathnames = dir.pathname.match(/[^/]+/g); 
  let _path = pathnames[0];

  if (!paths.hasOwnProperty(_path)) {
    paths[_path] = {child_total:0, statuses:[]}
  } 

  paths[_path].statuses.push({[dir.pathname]:status});

  if (pathnames.length > 1) {
    paths[_path].child_total += 1;
  };
  
  return paths;
}, {});

console.log(pathData);
guest271314
  • 1
  • 15
  • 104
  • 177
  • hmm not exactly what I'm aiming for, I need to know at the parent level the total number of children with status: 0, or status: 1. Reduce looks like an interesting approach, but as each value doesn't always relate to previous it might not be any more efficient – Kim T Dec 23 '16 at 15:10