简体   繁体   中英

Tail recursive JSON constructor

I have a directory structure from NPM package ' directory-tree ' that I would like to flatten into a simpler nested structure. I want a tail recursive solution to convert the first object into the second, but I'm having trouble wrapping my head around how to construct it.

Of course the primary conditional is whether or not a 'node' in the first structure is a 'file' or a 'directory'. If it's a file, we simply want the basename of the file to key the relative path. However, if it's a directory, we want the basename of the directory to key an object and recurse down frome there.

I'll use their example to illustrate the structure:

 { "path": "photos", "name": "photos", "size": 600, "type": "directory", "children": [ { "path": "photos/summer", "name": "summer", "size": 400, "type": "directory", "children": [ { "path": "photos/summer/june", "name": "june", "size": 400, "type": "directory", "children": [ { "path": "photos/summer/june/windsurf.jpg", "name": "windsurf.jpg", "size": 400, "type": "file", "extension": ".jpg" } ] } ] }, { "path": "photos/winter", "name": "winter", "size": 200, "type": "directory", "children": [ { "path": "photos/winter/january", "name": "january", "size": 200, "type": "directory", "children": [ { "path": "photos/winter/january/ski.png", "name": "ski.png", "size": 100, "type": "file", "extension": ".png" }, { "path": "photos/winter/january/snowboard.jpg", "name": "snowboard.jpg", "size": 100, "type": "file", "extension": ".jpg" } ] } ] } ] } 

I'd like the final structure to be much simpler. Something like the following:

 { "photos": { "summer": { "june": { "windsurf.jpg": "photos/summer/june/windsurf.jpg" } }, "winter": { "january": { "ski.png": "photos/winter/january/ski.png", "snowboard.jpg": "photos/winter/january/snowboard.jpg" } } } } 

We can convert a depth-first search to tail-recursion for your case.

 let testObj = { "path": "photos", "name": "photos", "size": 600, "type": "directory", "children": [ { "path": "photos/summer", "name": "summer", "size": 400, "type": "directory", "children": [ { "path": "photos/summer/june", "name": "june", "size": 400, "type": "directory", "children": [ { "path": "photos/summer/june/windsurf.jpg", "name": "windsurf.jpg", "size": 400, "type": "file", "extension": ".jpg" } ] } ] }, { "path": "photos/winter", "name": "winter", "size": 200, "type": "directory", "children": [ { "path": "photos/winter/january", "name": "january", "size": 200, "type": "directory", "children": [ { "path": "photos/winter/january/ski.png", "name": "ski.png", "size": 100, "type": "file", "extension": ".png" }, { "path": "photos/winter/january/snowboard.jpg", "name": "snowboard.jpg", "size": 100, "type": "file", "extension": ".jpg" } ] } ] } ] }; function tailRecurse(stack, result){ if (!stack.length) return result; // stack will contain // the next object to examine [obj, ref] = stack.pop(); if (obj.type == 'file'){ ref[obj.name] = obj.path; } else if (obj.type == 'directory'){ ref[obj.name] = {}; for (let child of obj.children) stack.push([child, ref[obj.name]]); } return tailRecurse(stack, result); } // Initialise let _result = {}; let _stack = [[testObj, _result]]; console.log(tailRecurse(_stack, _result)); 

function copyNode(node, result = {}){
   if(node.type === "directory"){
      const folder = result[node.name] = {};
      for(const sub of node.children)
           copyNode(sub, folder);

  } else {
      result[node.name] = node.path;
  }
  return result;
}

This is a simple recursive approach, this is not tail call recursive as it is very difficult ( = not worth it ) to traverse a tree with only one tail call.

You could take a recursive approach by chekcing the type.

For 'directory' take an object and iterate the children.

Otherwise assign the path to the key with the given name.

 function fn(source, target) { if (source.type === 'directory') { target[source.name] = {}; (source.children || []).forEach(o => fn(o, target[source.name])); } else { target[source.name] = source.path; } } var source = { path: "photos", name: "photos", size: 600, type: "directory", children: [{ path: "photos/summer", name: "summer", size: 400, type: "directory", children: [{ path: "photos/summer/june", name: "june", size: 400, type: "directory", children: [{ path: "photos/summer/june/windsurf.jpg", name: "windsurf.jpg", size: 400, type: "file", extension: ".jpg" }] }] }, { path: "photos/winter", name: "winter", size: 200, type: "directory", children: [{ path: "photos/winter/january", name: "january", size: 200, type: "directory", children: [{ path: "photos/winter/january/ski.png", name: "ski.png", size: 100, type: "file", extension: ".png" }, { path: "photos/winter/january/snowboard.jpg", name: "snowboard.jpg", size: 100, type: "file", extension: ".jpg" }] }] }] }, target = {}; fn(source, target); console.log(target); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM