草庐IT

javascript - 从 JSON 创建树

coder 2025-01-11 原文

我是 Javascript 的新手,我上过一些类(class),但我仍在学习它,并且我正在努力从 JSON 制作一棵树。我在这里查看了其他答案,但我似乎无法理解 reduce、递归和 jquery thingys。所以我做了我自己的功能。

但首先,我的 JSON 如下所示:

var data = [{
   "id": 51,
   "name": "root"
}, {
  "id": 54,
  "name": "app",
  "parentId": 53
}, {
  "id": 55,
  "name": "text.txt",
  "parentId": 54
}, {
 "id": 53,
 "name": "share",
 "parentId": 52
}, {
 "id": 52,
 "name": "local",
 "parentId": 51
}];

这些函数处理 JSON 对象:

var treeNode = function(nodeId, name) {
    var children = [];

    this.nodeId = nodeId;
    this.name = name;
    this.parentNode = null;

    this.setParent = function(parentNode) {
       this.parentNode = parentNode;
    };
    this.addChild = function(node){
       children.push(node);
       node.setParent(this);
    };
};

var Tree = function() {
  this.nodes = [];
  this.findNodeById = function(nodeId) {
     for (var i=0; i<this.nodes.length; i++) {
        if (this.nodes[i].nodeId === nodeId) {
           return this.nodes[i];
        }
   }
   return null;
  };
  this.createNode = function(nodeId, name, parentNode) {
     var node = new treeNode(nodeId, name);
     if (parentNode) {
       parentNode.addChild(node);
     }
     this.nodes.push(node);
   }
};

function createTree(data) {

  var tree = new Tree();
  var temp = [];
  for (var i=0; i<data.length; i++) {
    var inputNode = data[i];
    var parentNode = inputNode.parentId ? tree.findNodeById(inputNode.parentId) : null;
    tree.createNode(inputNode.id, inputNode.name, parentNode);
  }
 return tree.nodes;
}

然后我调用函数:createTree(data);

因此,经过多次调试尝试制作函数等之后,我终于意识到我可能在某个地方犯了错误,因为现在 54 和 53 节点的父节点没有显示,我只是无法理解我做错了什么,我该如何解决?有人可以帮帮我吗?

非常感谢任何建议。

最佳答案

您的代码总体上看起来不错。问题是算法。
它与插入节点的顺序直接相关。

原因

你有一棵空树。
首先,插入节点 #51。现在,你有一个只有一个节点 #51 的树。
然后您尝试插入节点 #54 和父节点 #53...这不存在。

这里

var parentNode = inputNode.parentId ? tree.findNodeById(inputNode.parentId) : null;

您调用遍历树的 tree.findNodeById,找不到节点 53(它还不在树中),并返回 null

因此,您的第二个节点将 parentNode 设置为 null,而不是节点 #53。

解决方案

基本思想是,您始终需要确保对于每个被插入的节点,其父节点已经在树中。

如何实现?

我想到的最简单的输入数据解决方案是在插入之前按升序对 parentNode 上的数组进行排序。
它将保证您始终在父节点之后插入子节点,但它仅在节点按拓扑顺序编号时才有效。
这意味着 parentId 总是小于 id。例如,节点 5 不能有 ID 为 7 的父节点。

在这里,它适用于您的输入数据:

var data = [{
   "id": 51,
   "name": "root"
}, {
  "id": 54,
  "name": "app",
  "parentId": 53
}, {
  "id": 55,
  "name": "text.txt",
  "parentId": 54
}, {
 "id": 53,
 "name": "share",
 "parentId": 52
}, {
 "id": 52,
 "name": "local",
 "parentId": 51
}];


var treeNode = function(nodeId, name) {
    var children = [];

    this.nodeId = nodeId;
    this.name = name;
    this.parentNode = null;

    this.setParent = function(parentNode) {
       this.parentNode = parentNode;
    };
    this.addChild = function(node){
       children.push(node);
       node.setParent(this);
    };
};

var Tree = function() {
  this.nodes = [];
  this.findNodeById = function(nodeId) {
     for (var i=0; i<this.nodes.length; i++) {
        if (this.nodes[i].nodeId === nodeId) {
           return this.nodes[i];
        }
   }
   return null;
  };
  this.createNode = function(nodeId, name, parentNode) {
     var node = new treeNode(nodeId, name);
     if (parentNode) {
       parentNode.addChild(node);
     }
     this.nodes.push(node);
   }
};



function createTree(data) {
  // HERE, you sort your array by parentId ASC:
  data = data.sort(function(a, b) {
    return a.parentId - b.parentId;
  });

  var tree = new Tree();
  var temp = [];
  for (var i=0; i<data.length; i++) {
    var inputNode = data[i];
    var parentNode = inputNode.parentId ? tree.findNodeById(inputNode.parentId) : null;
    tree.createNode(inputNode.id, inputNode.name,  parentNode);
  }
 return tree.nodes;
}

console.log(createTree(data));

但是,如果您的节点是随机编号的,那么您将需要实现 topological sorting而不是按 parentId 进行简单排序。

附言

顺便说一句,您可能希望使用对象映射而不是数组,以免每次都遍历它。它将提供 O(n) 倍改进 - O(1) 而不是 O(n):

var Tree = function() {
  this.nodes = {};
  this.findNodeById = function(nodeId) {
     return nodes[nodeId];
  };
  this.createNode = function(nodeId, name, parentNode) {
     var node = new treeNode(nodeId, name);

     if (parentNode) {
       parentNode.addChild(node);
     }

     if (this.nodes[nodeId]) {
       throw new Error("There is already node with ID " + nodeId + " in the tree.");
     }

     this.nodes[nodeId] = node;
   }
};

关于javascript - 从 JSON 创建树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40373148/

有关javascript - 从 JSON 创建树的更多相关文章

  1. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  2. ruby-on-rails - 如何使用 Rack 接收 JSON 对象 - 2

    我有一个非常简单的RubyRack服务器,例如:app=Proc.newdo|env|req=Rack::Request.new(env).paramspreq.inspect[200,{'Content-Type'=>'text/plain'},['Somebody']]endRack::Handler::Thin.run(app,:Port=>4001,:threaded=>true)每当我使用JSON对象向服务器发送POSTHTTP请求时:{"session":{"accountId":String,"callId":String,"from":Object,"headers":

  3. ruby - 用 YAML.load 解析 json 安全吗? - 2

    我正在使用ruby2.1.0我有一个json文件。例如:test.json{"item":[{"apple":1},{"banana":2}]}用YAML.load加载这个文件安全吗?YAML.load(File.read('test.json'))我正在尝试加载一个json或yaml格式的文件。 最佳答案 YAML可以加载JSONYAML.load('{"something":"test","other":4}')=>{"something"=>"test","other"=>4}JSON将无法加载YAML。JSON.load("

  4. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  5. ruby-on-rails - Rails 渲染带有驼峰命名法的 json 对象 - 2

    我在一个简单的RailsAPI中有以下Controller代码:classApi::V1::AccountsControllerehead:not_foundendendend问题在于,生成的json具有以下格式:{id:2,name:'Simpleaccount',cash_flows:[{id:1,amount:34.3,description:'simpledescription'},{id:2,amount:1.12,description:'otherdescription'}]}我需要我生成的json是camelCase('cashFlows'而不是'cash_flows'

  6. ruby - 使用 JSON gem 将自定义对象转换为 JSON - 2

    我正在学习如何使用JSONgem解析和生成JSON。我可以轻松地创建数据哈希并将其生成为JSON;但是,在获取一个类的实例(例如Person实例)并将其所有实例变量放入哈希中以转换为JSON时,我脑袋放屁。这是我遇到问题的例子:require"json"classPersondefinitialize(name,age,address)@name=name@age=age@address=addressenddefto_jsonendendp=Person.new('JohnDoe',46,"123ElmStreet")p.to_json我想创建一个.to_json方法,这样我就可以获

  7. ruby-on-rails - 如何使用驼峰键名称从 Rails 返回 JSON - 2

    我正在构建一个带有Rails后端的JS应用程序,为了不混淆snake和camelcases,我想通过从服务器返回camelcase键名来规范化这一切。因此,当从API返回时,user.last_name将返回user.lastName。我如何实现这一点?谢谢!编辑:添加Controller代码classApi::V1::UsersController 最佳答案 我的方法是使用ActiveModelSerializer和json_api适配器:在你的Gemfile中,添加:gem'active_model_serializers'创建

  8. ruby-on-rails - 如何将数组输出为 JSON? - 2

    我有以下内容:@array.inspect["x1","x2","adad"]我希望能够将其格式化为:client.send_message(s,m,{:id=>"x1",:id=>"x2",:id=>"adad"})client.send_message(s,m,???????)如何在????????中获得@array输出?空间作为ID?谢谢 最佳答案 {:id=>"x1",:id=>"x2",:id=>"adad"}不是有效的散列,因为您有键冲突它应该是这样的:{"ids":["x1","x2","x3"]}更新:@a=["x1

  9. ruby - 使用 jbuilder 创建具有动态哈希键的 JSON - 2

    这里我想输出带有动态组名的json而不是单词组@tickets.eachdo|group,v|json.group{json.array!vdo|ticket|json.partial!'tickets/ticket',ticket:ticketend}end@ticket是这样的散列{a:[....],b:[.....]}我想要这样的输出{a:[.....],b:[....]} 最佳答案 感谢@AntarrByrd,这个问题有类似的答案:JBuilderdynamickeysformodelattributes使用上面的逻辑我已经

  10. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

    我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

随机推荐