Javascript递归导致循环结构

Posted

技术标签:

【中文标题】Javascript递归导致循环结构【英文标题】:Javascript recursion caused circular structure 【发布时间】:2017-03-03 01:49:40 【问题描述】:

我有以下二维数组cells

ID  Name    Parent
1   Bob     0
2   Alice   1
3   John    2
4   Jane    2
5   Jenny   3
6   Jonny   2

我想把它转换成嵌套的 JSON 格式,这样每个对象都有以下属性:

    姓名

    子数组,其中还包括具有名称的对象和子数组。数组中没有循环嵌套;孩子不能有任何父母作为孩子。

这是我写的函数:

function getChildren(node)
    console.log('Getting Children for' + node)
    children = []
    for(i = 0;i < cells.length; i++)
        if(cells[i][2] == node)
            cell = cells[i]
            child = 
            child.name = cell[1]
            child.children = getChildren(cell[0])
            children.push(child)
        
    
    return children



text = "ID  Name    Parent\n1   Bob     0\n2   Alice   1\n3   John                  2\n4   Jane    2\n5   Jenny     3\n6   Jonny   2\n"

lines = text.split('\n')
cells = []
for(i = 0; i < lines.length; i++)
    cells[i] = lines[i].split(/\ +/)

在节点 6 getChildren(6) 上调用函数,得到以下输出:

让孩子6岁 []

这是正确的,因为节点 6 没有子节点。

但是在有子节点的节点上调用函数,例如getChildren(3),给出:

让孩子为 3 让孩子 5

Object
    children: Array[1]
        0: Object
            children: Array[1]
                0: Object
                    children: Array[1]
                    name: "Jenny"
                    length: 1
            name: "Jenny"
            length: 1
    name: "Jenny"

从控制台输出看来,它调用了正确的函数,但为什么“jenny”的对象无限嵌套在所有子项下?

我想最终得到一个可以使用 JSON.stringify 的 JSON 对象。在getChildren(3) 上调用函数会报错

未捕获的 TypeError:将循环结构转换为 JSON。

我认为这是因为 Jenny 的对象无限嵌套在每个孩子之下。

【问题讨论】:

请以合法的 javascript 数据定义或字符串格式显示您开始使用的实际 Javascript 数据结构以及您希望最终得到的数据结构。显示一个表格并不能准确地告诉我们它在代码中的结构。我们可以猜测,但你应该透露足够的信息,我们不必猜测。 它在小提琴链接中,我从一个解析成表格的字符串开始,最后我想将它转换为 json,每个对象都有一个名称,以及一个子数组,它们也是具有名称的对象和子数组 根据本网站的指南,它应该直接粘贴到您的问题中,而不仅仅是在 jsFiddle 链接中。外部链接会随着时间的推移而改变或消失,从而破坏问题的最重要内容,从而使问题作为长期参考的用处大大降低。另外,您是否意识到 JSON 是一种字符串格式,而不是一种对象格式。这真的是你想要的吗?您应该显示您开始的确切数据定义和您想要结束的确切数据定义,然后您只需要几句话就可以 100% 清楚。 全局变量可能也无济于事,尤其是考虑到您使用的是递归函数。 好的,我添加了完整的代码,以及我想要的最终输出 【参考方案1】:

将您的功能更改为以下

function getChildren(node)
    console.log('Getting Children for' + node)
    var children = []
    for(i = 0;i<cells.length; i++)
        if(cells[i][2] == node)
            var cell = cells[i]
            child = 
            child.name = cell[1]
            child.children = getChildren(cell[0])
            children.push(child)
        
    return children

注意变量声明前添加的“var”。这确保它们被重新初始化,而不是通过函数调用持续存在。这就是导致您的问题的原因。

【讨论】:

【参考方案2】:

您使用全局变量,因此,当您递归调用函数时,childrenchild 等变量可以获得新值。当您退出递归调用时,您可以:

children.push(child)

.. 但是 children 将采用另一个值而不是您预期的值,因此 child 也可以具有与递归调用中的值不同的值(或者从更深层次的递归)。

出于同样的原因,i的递归修改会导致问题。

使用var 将变量设置为函数的本地变量,它会起作用:

function getChildren(node)
    console.log('Getting Children for' + node)
    var children = []
    for(var i = 0;i<cells.length; i++)
        if(cells[i][2] == node)
            var cell = cells[i]
            var child = 
            child.name = cell[1]
            child.children = getChildren(cell[0])
            children.push(child)
        
    return children

function getChildren(node)
    var children = []
    for(var i = 0;i<cells.length; i++)
        if(cells[i][2] == node)
            var cell = cells[i]
            var child = 
            child.name = cell[1]
            child.children = getChildren(cell[0])
            children.push(child)
        
    return children


var text = "ID  Name    Parent\n1   Bob     0\n2   Alice   1\n3   John                  2\n4   Jane    2\n5   Jenny     3\n6   Jonny   2\n"

var lines = text.split('\n')
var cells = []
for(var i = 0; i< lines.length; i++)
  cells[i] = lines[i].split(/\ +/)


console.log(JSON.stringify(getChildren(0), null, 2));
.as-console-wrapper  max-height: 100% !important; top: 0; 

【讨论】:

感谢您解释 var 部分,但是当我在第一个父级上调用该函数时:JSON.stringify(getChildren(0)) 输出不包括具有父级 John 的子 Jenny 它给出:"["name":"Bob","children":["name":"Alice","children":["name":"John","children":[],"name":"Jane","children":[],"name":"Jonny","children":[]]]]" 我不明白。我在答案中添加了一个 sn-p,如果您使用参数 0 调用函数,则表明 Jenny 在输出中。【参考方案3】:

如果我理解正确,您可以使用另一种方式来解析您的数据:

//separete the values
var input = [
    [1,   'Bob',     0],
    [2,   'Alice',   1],
    [3,   'John',    2],
    [4,   'Jane',    2],
    [5,   'Jenny',   3],
    [6,   'Jonny',   2]
];

var entities = []; //I belive that the ids are uniq

for (var i = 0; i < input.length; i++)
    var id = input[i][0];
  entities[id] = 
    id: id, 
    name: input[i][1], 
    children : [], 
    parent: input[i][2]
  ;


for (var i in entities)
    var current = entities[i];
  var parent = entities[current.parent];
  delete current.parent;

  if (parent)
        parent.children.push(current);
  

通过这种方式,您可以通过索引从实体数组中找到特定元素,或者在解析开始时获取根元素(元素数组中不包含其父元素的元素)

【讨论】:

以上是关于Javascript递归导致循环结构的主要内容,如果未能解决你的问题,请参考以下文章

linux shell for循环解决递归循环目录结构

高性能 JavaScriptの笔记-- 算法与流程控制

什么是循环结构?

Python基础-----while循环练习

这期我们来谈谈递归和循环

JavaScript的循环结构和经典题目