【问题标题】:Javascript array gets populated and i don't understand howJavascript数组被填充,我不明白如何
【发布时间】:2020-06-29 11:23:40
【问题描述】:

在搜索我遇到的关于邻接列表中的树结构的问题的解决方案时。我遇到了一个为我解决了它的答案,但我无法理解它是如何工作的,我觉得我缺少对 javascript 的一些基本了解。

@TreyE 在this question 中的回答给出了排序树的下一个解决方案 ->

var flat = [
    { id: 1, name: "Business", parent: 0 },
    { id: 2, name: "Management", parent: 1 },
    { id: 3, name: "Leadership", parent: 2 },
    { id: 4, name: "Finance", parent: 1 },
    { id: 5, name: "Fiction", parent: 0 },
    { id: 6, name: "Accounting", parent: 1 },
    { id: 7, name: "Project Management", parent: 2  }
];

var nodes = [];
var toplevelNodes = [];
var lookupList = {};

for (var i = 0; i < flat.length; i++) {
    var n = {
        id: flat[i].id,
        name: flat[i].name,
        parent_id: ((flat[i].parent == 0) ? null : flat[i].parent),
        children: []
        };
    lookupList[n.id] = n;
    nodes.push(n);
    if (n.parent_id == null) {
        toplevelNodes.push(n);
    }
}

for (var i = 0; i < nodes.length; i++) {
  var n = nodes[i];
  if (!(n.parent_id == null)) {
      lookupList[n.parent_id].children = lookupList[n.parent_id].children.concat([n]);
  }
}

console.log(toplevelNodes);

这工作得很好,但如果唯一被推送到它的东西是第一级元素,我就是无法理解所有这些逻辑是如何在toplevelNodes 下排序的。其余的在nodeslookuplist 上完成。我再次觉得我缺少对 javascript 的一些基本理解。 topLevelNodes 数组下的所有东西如何排序成一棵漂亮的儿童树?

【问题讨论】:

  • nodestoplevelNodes 在执行最后一个循环之前都包含相同的对象。 push 不复制对象,n 引用两个数组中的同一个对象。
  • 我建议您使用console.dir(variable)查看数据并尝试自己弄清楚。

标签: javascript arrays


【解决方案1】:

秘密在于lookupList,即Object。存储在该对象中的项目(或 活动项目)不是深度克隆,它们只是 “指针”(不完全指向 C 中的内存地址),指向现有项目记忆,作为参考。如果一个节点发生变化(即:通过向一个节点添加更多子节点),对该元素的“引用”也会被修改,因为它实际上是同一个节点

就在这里:

for (var i = 0; i < nodes.length; i++) {
  var n = nodes[i];
  if (!(n.parent_id == null)) {
      lookupList[n.parent_id].children = lookupList[n.parent_id].children.concat([n]);
  }
}

下面是一个非常简单的例子:

const node_1 = {id: 1, name: "foo", children: []};
const node_2 = {id: 2, name: "bar", children: []};

const lookupList = {};

// store node_1 into lookup - as reference:
lookupList[node_1.id] = node_1;

// (Unrelated to lookupList) add node_2 as child of node_1
node_1.children.push(node_2); 

// Let's sniff lookup for node_1 (id: 1)
console.log(lookupList["1"]); // There's also node_2 as child of node_1

console.log(lookupList["1"] === node_1); // true (it's the same Node)

【讨论】:

  • Tnx @roko,我完全忘记了指针和活的东西。现在一切都说得通了。
【解决方案2】:

在指向对象或数组的 JS 变量中,保存对该对象的引用,而不是它的副本。

如果您有多个变量指向同一个对象并通过一个变量改变对象,则另一个引用下的对象也会发生改变(因为两个变量都指向同一个对象):

var a = {hello: 'a'}
var b = a
b.hello = 'b'
console.log(a.hello)
// prints 'b'

同样的事情发生在您的代码中:nodestoplevelNodeslookupList 都在其中包含对相同节点对象的引用,当您在一处进行变异时,它也会在其他地方更新。

基本上这就是行之有效的方法:

lookupList[n.parent_id].children = ...

如果您正在寻找对该主题的更深入了解,请查看此部分:https://eloquentjavascript.net/04_data.html#h_C3n45IkMhg

【讨论】:

  • 哇,输入完答案后发现已经有 3 个答案可用了……也许我对 Stackoverflow 的事情有点太慢了……
【解决方案3】:

lookupList 将 id 映射到它们的节点,因为这是在第一个循环中添加到它的:lookupList[n.id] = n

在第二个循环中,每个不是顶级节点的节点都被添加到其父节点的子数组中。在 lookupList (lookupList[n.parent_id]) 中查找节点的 parent_id 属性来检索其父节点

【讨论】:

    【解决方案4】:

    也许另一种方法更容易理解?这将在原地修改 nodes (née flat):

    /**
      Augment each node in the "nodes" list with a `children` property,
      and return the top-level nodes.
    */
    function treeify(nodes) {
      const children = {};
    
      // Walk through the flat list of nodes once...
      nodes.forEach((n) => {
        // Retrieve or initialize a list for `n.parent`'s children.
        const childList = children[n.parent] || (children[n.parent] = []);
        // Add this node there.
        childList.push(n);
      });
    
      // ... and walk through it again, to assign the new `children` property.
      nodes.forEach((n) => {
        // Pick each "children" property from the children map,
        // or in case there is none, come up with an empty list.
        n.children = children[n.id] || [];
      });
    
      // Nodes with parent 0 are top-level; return them.
      return children[0];
    }
    
    const nodes = [
      { id: 1, name: "Business", parent: 0 },
      { id: 2, name: "Management", parent: 1 },
      { id: 3, name: "Leadership", parent: 2 },
      { id: 4, name: "Finance", parent: 1 },
      { id: 5, name: "Fiction", parent: 0 },
      { id: 6, name: "Accounting", parent: 1 },
      { id: 7, name: "Project Management", parent: 2 },
    ];
    
    const tree = treeify(nodes);
    
    // Node.js specific printing stuff...
    const util = require("util");
    console.log(util.inspect(tree, null, { depth: 5 }));
    

    这个输出

    [
      {
        id: 1,
        name: 'Business',
        parent: 0,
        children: [
          {
            id: 2,
            name: 'Management',
            parent: 1,
            children: [
              { id: 3, name: 'Leadership', parent: 2, children: [] },
              {
                id: 7,
                name: 'Project Management',
                parent: 2,
                children: []
              }
            ]
          },
          { id: 4, name: 'Finance', parent: 1, children: [] },
          { id: 6, name: 'Accounting', parent: 1, children: [] }
        ]
      },
      { id: 5, name: 'Fiction', parent: 0, children: [] }
    ]
    

    【讨论】:

      猜你喜欢
      • 2013-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-26
      • 1970-01-01
      • 1970-01-01
      • 2014-03-01
      • 1970-01-01
      相关资源
      最近更新 更多