4

I'm trying to take a flat array of paths, and create nested array of objects. The trouble i'm having is with the recursive part of generating children nodes...

Starting array:

const paths = [ 
  '/',
  '/blog',
  '/blog/filename',
  '/blog/slug',
  '/blog/title',
  '/website',
  '/website/deploy',
  '/website/infrastructure',
  '/website/infrastructure/aws-notes',
];

With a desired output structure:

[
  { 
    path: '/',
  },
  {
    path: '/blog',
    children: [
      {
        path: '/blog/filename',
      },
      {
        path: '/blog/slug',
      },
      {
        path: '/blog/title',
      }
    ]
  },
  { 
    path: '/website',
    children: [
      {
        path: '/website/deploy',
      },
      {
        path: '/website/infrastructure',
        children: [
          {
            path: '/website/infrastructure/aws-notes',
          }
        ],
      },
    ],
  },
]

Here's where i'm at so far, i've tried a few things but ultimately ends in infinite loops or poor structure:

const getPathParts = (path) => path.substring(1).split('/');
const getPathLevel = (path) => getPathParts(path).length - 1;
const getTree = (paths) => paths.reduce((tree, path, i, paths) => {
  const pathParts = getPathParts(path);
  const pathDepth = getPathLevel(path);
  const current = pathParts[pathDepth];
  const parent = pathParts[pathDepth - 1] || null;
  const item = {
    path,
    children: [],
  };

  if (pathDepth > 0 || parent !== null) {
    // recursive check for parent, push this as a child to that parent?
    return [...tree];
  }

  return  [
    ...tree, 
    item,
  ];
}, []);

I've tried array.find|some|filter to retrieve the parent, but i'm at a loss how to push the node as a child into the correct nested node. NOTE: i've extracted some code for the example, pardon any syntax/spelling issues.

1 Answer 1

1

You could take a nested approach by taking the pathes and split them and check if the object with the path already exists or not. If not push a new object.

Later return the children of the actual object. Proceeed until no more path items are available.

const
    paths = ['/', '/blog', '/blog/filename', '/blog/slug', '/blog/title', '/website', '/website/deploy', '/website/infrastructure', '/website/infrastructure/aws-notes'],
    result = paths.reduce((r, path) => {
        path.split(/(?=\/)/).reduce((a, _, i, p) => {
            var temp = a.find(o => o.path === p.slice(0, i + 1).join(''));
            if (!temp) {
                a.push(temp = { path, children: [] });
            }
            return temp.children;
        }, r);
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

For creating by using only pathes to final directories, you could take parts of the path for creating.

This approach prevents empty children arrays.

const
    paths = [
        '/',
        '/blog/filename',
        '/blog/slug',
        '/blog/title',
        '/website/deploy',
        '/website/infrastructure/aws-notes'
    ],
    result = [];

paths.reduce((r, string) => {
    string.split(/(?=\/)/).reduce((o, _, i, p) => {
        o.children = o.children || [];
        var path = p.slice(0, i + 1).join(''),
            temp = o.children.find(o => o.path === path);
        if (!temp) {
            o.children.push(temp = { path });
        }
        return temp;
    }, r);
    return r;
}, { children: result });

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

1 Comment

@gordie, sorry. please ask a new question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.