0

Say I have the following strings:

"files/photos/foo.png"

"files/videos/movie.mov"

and I want to convert them to the following object:

{
    name: "files"
    children: [{
        name: "photos",
        children: [{
            name: "foo.png",
            id: "files/photos/foo.png"
        }]
    },{
        name: "videos",
        children: [{
            name: "movie.mov",
            id: "files/videos/movie.mov"
        }]
    }]
}

What would be the best approach for doing so? I've tried writing some recursive functions, however admit that I'm struggling at the moment.

2
  • Sorry, first was C#, see: Javascript: How to create an object from a dot separated string?. While the final shape of the objects in the duplicate is different the method is applicable. Also, tree from array of dot-separated strings Commented Apr 25, 2022 at 21:58
  • @pilchard thanks for the references, those are close to what I'm looking for. Unfortunately I need to be able to combine multiple strings into one object, which doesn't look possible with those solutions. Commented Apr 25, 2022 at 22:04

2 Answers 2

1

Here's a quick snippet with a possible solution. It uses nested loops, the outer splitting each path by the delimeter and pop()ing the file portion out of the array. The inner iterates the parts of the path and constructs the heirarchy by reasigning branch on each iteration. Finally the file portion of the path is added to the deepest branch.

const data = [
  'files/photos/foo.png',
  'files/photos/bar.png',
  'files/videos/movie.mov',
  'docs/photos/sd.jpg'
];

const tree = { root: {} }
for (const path of data) {

  const parts = path.split('/');
  const file = parts.pop();

  let branch = tree, partPath = '';
  for (const part of parts) {
    partPath += `${part}/`;

    if (partPath === `${part}/`) {
      tree.root[partPath] = (tree[partPath] ??= { name: part, children: [] });
    } else if (tree[partPath] === undefined) {
        tree[partPath] = { name: part, children: [] };
        branch.children.push(tree[partPath]);
    }

    branch = tree[partPath];
  }

  branch.children.push({ name: file, id: path });
}

const result = Object.values(tree.root)

console.log(JSON.stringify(result, null, 2))
.as-console-wrapper { max-height: 100% !important; top: 0; }
.as-console-row::after { display: none !important; }

Or as a function.

function mergeAssets(assets) {
  const tree = { root: {} }

  for (const path of data) {
    const parts = path.split('/');
    const file = parts.pop();

    let branch = tree, partPath = '';
    for (const part of parts) {
      partPath += `${part}/`;

      if (partPath === `${part}/`) {
        tree.root[partPath] = (tree[partPath] ??= { name: part, children: [] });
      } else if (tree[partPath] === undefined) {
        tree[partPath] = { name: part, children: [] };
        branch.children.push(tree[partPath]);
      }

      branch = tree[partPath];
    }

    branch.children.push({ name: file, id: path });
  }

  return {
    name: "assets",
    children: Object.values(tree.root)
  }
}

const data = [
  'files/photos/foo.png',
  'files/photos/bar.png',
  'files/videos/movie.mov',
  'docs/photos/sd.jpg'
];

const result = mergeAssets(data);

console.log(JSON.stringify(result, null, 2))

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

Comments

0

I was able to find a solution using a recursive function. If others have any tips on how to improve this, I'd love to hear.

function mergeObjects(parentArray,path,originalName){
    if(originalName === undefined){
        originalName = path;
    }

    const parts = path.split("/");
    var nextPart = "";
    parts.forEach((part, index) => index > 0 ? nextPart += (nextPart !== "" ? "/" : "") + part : null);
    //does the parentArray contain a child with our name?
    const indexOfChild = parentArray.findIndex(child => child.name === parts[0]);
    if(indexOfChild === -1){
        //this item does not exist
        if(parts.length > 1){
            var index = parentArray.push({
                name: parts[0],
                children : []
            }) - 1;
            mergeObjects(parentArray[index].children,nextPart,originalName);
        }else{
            parentArray.push({
                name: parts[0],
                id : originalName
            });
        }
    }else{
        //this item already exists
        if(parts.length > 1){
            mergeObjects(parentArray[indexOfChild].children,nextPart,originalName);
        }
    }
}

And the function is called with the following:

function mergeAssets(assets){
    var obj = {
        name: "assets",
        children: []
    };
    assets.forEach(asset => mergeObjects(obj.children,asset));
    return obj;
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.