【问题标题】:Fill an array recursively walking a DOM tree以递归方式填充数组,遍历 DOM 树
【发布时间】:2015-10-22 20:13:16
【问题描述】:

当我从一个节点递归遍历一个通用树到他的所有孩子时,我必须填充一个字符串数组。在实践中,在从节点到叶子匹配的每个节点处,在 DOM 树中插入一个字符串。 我知道这是一个微不足道的问题,但我无法解决。 这是我写的代码:

function operationsToInsert(node) {
   var operations = [];
   operationsToInsertRec(node, operations);
   return operations;
}

function operationsToInsertRec(node, operations) {
   var childNodes = node.childNodes;
   operations.push("i(" + node.nodeName + ") ");
   for(var i = 0; i < childNodes.length; i++) {
      operationsToInsertRec(childNodes[i], operations);
      operations.push("i(" + childNodes[i].nodeName + ")");
   }   
}

但是有如下错误:

Uncaught TypeError: Cannot read property 'push' of undefined at line operations.push("insert(" + node.nodeName + ") ");

我该如何解决? 谢谢

【问题讨论】:

  • 在你的 for 循环的第一行,你没有检索到 operationsToInsert 返回的值。
  • @James 你能解释得更好吗?没看懂。。

标签: javascript arrays recursion


【解决方案1】:

这是一种使用方便的 Array.prototype.reduce 函数来遍历树的方法,它使用了一种技巧,可以让它在类似数组的情况下工作:

function flatten(ops, n) {
    ops.push("i(" + n.nodeName + ") ");
    if (n.childNodes && n.childNodes.length) {
       [].reduce.call(n.childNodes, flatten, ops);
    }
    return ops;
}

var node = document.getElementById("start_here");
var ops = [node].reduce(flatten, []);

fiddle

【讨论】:

  • 谢谢,我以这种方式修改了我的代码function operationsToInsert(node) { var operations = [node].reduce(operationsToInsertRec, []); return operations; } function operationsToInsertRec(node, operations) { operations.push("i(" + node.nodeName + ")"); if(node.childNodes &amp;&amp; node.childNodes.length) { [].reduce.call(node.childNodes, operationsToInsertRec, operations); } return operations; },但我遇到了错误Uncaught TypeError: operations.push is not a function
  • 您的参数设置错误。 operationsToInsertRec 函数需要接受 (operations, node) ,而不是 (node, operations)
  • 谢谢,如果我想得到一个如上所述的数组,但填充了按自下而上顺序遍历的节点?像 [b, a, d, c, node][d, c, b, a, node]
  • 您可以将“reduce”替换为“reduceRight”以从下往上遍历树 - 应该可以。
【解决方案2】:

问题是你只有一个功能,它不是你想象的那样!你重新定义了它,所以当你调用你认为的第一个参数时,你只提供了一个参数,其余参数隐式为undefined

这是您的相同代码,简化为可演示的示例:

function operationsToInsert(node) {
   console.log("Definition #1");
}

function operationsToInsert(node, operations) {
    console.log("Definintion #2");
}

operationsToInsert();

您需要更改其中一个函数的名称,以免发生冲突。



编辑以解决新问题:

我认为您是说您的新问题是大多数节点在列表中出现两次。跟踪代码,您会看到除根节点之外的每个节点都处理了两次。在 operationsToInsertRec() 中,您将节点放入列表 (childNodes[i]),然后将其传递给 operationsToInsertRec(),然后将其放入列表 (node)。

这里有一个简单的改变来解决这个问题:

function operationsToInsert(node) {
   var operations = [];
   operations.push("i(" + node.nodeName + ") ");
   operationsToInsertRec(node, operations);
   return operations;
}

function operationsToInsertRec(node, operations) {
   var childNodes = node.childNodes;
   for(var i = 0; i < childNodes.length; i++) {
      operationsToInsertRec(childNodes[i], operations);
      operations.push("i(" + childNodes[i].nodeName + ")");
   }   
}

operationsToInsert() 我推送根节点。那么operationsToInsertRec() 只处理子节点,因此每个节点只处理一次。

在对不同答案的评论中,我看到您谈到了遍历顺序的主题。在遍历树时,这些算法有几种不同的分类:depth-first,又细分为pre-orderin-order后序广度优先。您可以在tree traversal 上的维基百科文章中找到更多信息。

【讨论】:

  • 感谢您的回复。这是一个问题,现在(通过重命名第二种方法)不再有错误。但是,该方法并没有做它应该做的事情。返回的数组的元素比它应该的要多(也就是说,数量等于大小,即它的所有子元素的数量+他自己)..
  • 遍历树是错误的,因为内部节点被访问了几次然后该方法添加了更多关于这些节点的字符串,而每个节点都应该有一个字符串
猜你喜欢
  • 1970-01-01
  • 2018-07-12
  • 2014-03-11
  • 1970-01-01
  • 2011-07-21
  • 1970-01-01
  • 2011-07-17
  • 2016-02-22
  • 1970-01-01
相关资源
最近更新 更多