【问题标题】:Tail recursive JSON constructor尾递归 JSON 构造函数
【发布时间】:2018-06-24 06:54:42
【问题描述】:

我有一个来自 NPM 包“directory-tree”的目录结构,我想将其展平为更简单的嵌套结构。我想要一个尾递归解决方案来将第一个对象转换为第二个对象,但是我在思考如何构建它时遇到了麻烦。

当然,主要条件是第一个结构中的“节点”是“文件”还是“目录”。如果它是一个文件,我们只需要文件的基本名称作为相对路径的键。但是,如果它是一个目录,我们希望目录的基本名称作为一个对象的键并从那里向下递归。

我会用他们的例子来说明结构:

{
  "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"
            }
          ]
        }
      ]
    }
  ]
}

我希望最终的结构更简单。类似于以下内容:

{
    "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"
            }
        }
    }
}

【问题讨论】:

  • 我看不出你的“简单”结构有什么好处。而且我认为这里不可能使用尾调用递归。
  • 您为什么关心重塑数据?由于基于值的键,您提出的结果比原来的结果要差得多。重塑数据后,您将如何处理数据?显示文件和目录列表?搜索匹配字符串的文件?这是你真正的问题,而不是你在这里写的任何内容。
  • 抱歉,我问了一个技术问题。不是关于如何编写我的程序的建议。我真正的问题是完全正确,正如我所写和措辞的那样。我的朋友 StackOverflow 的问题是,如果你写一个关于风格或算法方法的问题,它“太笼统”了,会被忽略或删除。如果您编写的问题过于特殊或特定于库,则不会获得任何意见。请相信我们中的一些人知道这个网站不是让 CS101 孩子获得家庭作业建议的地方。我不需要解释我的程序做了什么以及为什么这个数据结构需要这样。

标签: javascript json node.js recursion tail-recursion


【解决方案1】:

我们可以根据您的情况将深度优先搜索转换为尾递归。

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

【讨论】:

  • 非常优雅的引用传递....我没想到会这样做。
  • @W4t3randWind 谢谢!这种堆栈实现通常被视为while (stack.length) 循环。使其成为显式递归实际上只是运行同一件事的另一种方式:)
【解决方案2】:

您可以通过检查类型来采用递归方法。

对于'directory',获取一个对象并迭代孩子。

否则将路径分配给具有给定名称的键。

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

【讨论】:

    【解决方案3】:
    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;
    }
    

    这是一种简单的递归方法,这不是尾调用递归,因为只有一个尾调用遍历一棵树非常困难(= 不值得)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-14
      • 1970-01-01
      • 1970-01-01
      • 2016-01-06
      • 2020-09-16
      • 1970-01-01
      • 2016-06-13
      • 1970-01-01
      相关资源
      最近更新 更多