Рекурсия Javascript вызвала круговую структуру

У меня есть следующий 2D-массив cells:

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

Я хочу преобразовать его во вложенный формат JSON, чтобы каждый объект имел следующие атрибуты:

  1. название

  2. Массив дочерних элементов, в который также входят объекты с именами и массивы детей. В массиве нет круговых вложений; ребенок не может иметь родителей в детстве.

Вот функция, которую я написал:

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, на котором я мог бы использовать JSON.stringify. Вызов функции на getChildren(3) дает ошибку

Uncaught TypeError: преобразование круговой структуры в JSON.

Я думаю, это потому, что объект для Jenny бесконечно вложен под каждого ребенка.


person Mohab    schedule 20.10.2016    source источник
comment
Пожалуйста, покажите фактическую структуру данных Javascript, с которой вы начинаете, и то, что вы хотите получить в конечном итоге, в допустимом определении данных Javascript или в строковом формате. Отображение таблицы не говорит нам точно, как она структурирована в коде. Мы можем догадываться, но тогда вы должны раскрыть достаточно, чтобы нам не приходилось гадать.   -  person jfriend00    schedule 21.10.2016
comment
Он находится в ссылке на скрипт, я начинаю со строки, которую я анализирую в таблицу, я хочу преобразовать ее в json в конце, у каждого объекта есть имя и массив дочерних элементов, которые также являются объектами с именами, и массивы детей   -  person Mohab    schedule 21.10.2016
comment
Согласно рекомендациям для этого сайта, его следует вставить прямо в ваш вопрос, а не только в ссылку jsFiddle. Внешние ссылки имеют привычку меняться или исчезать со временем, портя самое важное содержание вопроса и, таким образом, делает вопрос гораздо менее полезным в качестве долгосрочной ссылки. Кроме того, вы понимаете, что JSON - это строковый, а не объектный формат. Вы действительно этого хотите? Вы должны показать точное определение данных, с которого вы начинаете, и точное определение данных, которым вы хотите закончить, и тогда вам понадобится всего несколько слов вокруг этого, чтобы быть на 100% ясным.   -  person jfriend00    schedule 21.10.2016
comment
глобальные переменные, вероятно, тоже не помогают, особенно если учесть, что вы используете рекурсивную функцию.   -  person Kevin B    schedule 21.10.2016
comment
Хорошо, я добавил полный код и окончательный результат, который я хочу получить   -  person Mohab    schedule 21.10.2016
comment
@ jfriend00 не надо быть таким грубым.   -  person Matti Price    schedule 21.10.2016
comment
@Mohab - как вы понимаете, JSON - это строковый формат, описанный здесь. Поскольку вы говорите, что хотите иметь возможность вызывать JSON.stringify() на выходе, очевидно, вам нужен объект Javascript, который затем можно было бы преобразовать в строку, чтобы получить от него JSON. Это распространенная ошибка, поскольку люди склонны использовать JSON для описания объекта Javascript, но это совсем не одно и то же, и нам важно знать, о чем вы на самом деле просите. Я предполагаю, что на самом деле вам нужен объект Javascript.   -  person jfriend00    schedule 21.10.2016
comment
@MattiPrice - Точка взята, но я пытался объяснить, почему описание структуры данных в словах 2D-массив просто не совсем понятно, тогда как показ фактического определения данных на 100% ясен - не только для этого вопроса, но и поскольку OP кажется довольно новым, это тоже информация для будущих вопросов.   -  person jfriend00    schedule 21.10.2016
comment
@ jfriend00 Я не возражаю, но грубость также может отбивать у людей охоту задавать вопросы и узнавать, о чем в конечном итоге и идет речь на сайте.   -  person Matti Price    schedule 21.10.2016
comment
@MattiPrice - Да, но количество вопросов низкого качества здесь слишком велико, поэтому кто-то должен обучать этих новичков тому, что представляет собой четкий, краткий и полный вопрос. Да, иногда я мог бы проявить немного больше такта, но терпение иногда истощается, когда вы читаете 10-й вопрос подряд, который просто не содержит достаточно информации, чтобы на самом деле ответить на него. Для протокола, я отвечаю на массу вопросов здесь и очень помогаю людям своими комментариями.   -  person jfriend00    schedule 21.10.2016


Ответы (3)


Вы используете глобальные переменные, и, как следствие, при рекурсивном вызове функции такие переменные, как children и child, могут получать новые значения. Когда вы выходите из рекурсивного вызова, вы делаете:

children.push(child)

.. но потомки будут принимать другое значение, чем вы ожидаете, и поэтому 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; }

person trincot    schedule 20.10.2016
comment
Спасибо за объяснение части var, но когда я вызываю функцию для первого родителя: JSON.stringify(getChildren(0)) вывод не включает дочернюю Дженни, у которой есть родитель Джон. Он дает: "[{"name":"Bob","children":[{"name":"Alice","children":[{"name":"John","children":[]},{"name":"Jane","children":[]},{"name":"Jonny","children":[]}]}]}]" - person Mohab; 21.10.2016
comment
Я не понимаю. Я добавил в свой ответ фрагмент, который показывает, что Дженни находится на выходе, если вы вызываете функцию с аргументом 0. - person trincot; 21.10.2016

Измените свою функцию на следующую

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" перед объявлениями переменных. Это гарантирует, что они будут повторно инициализированы, а не сохраняться через вызовы функций. Это было причиной вашей проблемы.

person Matti Price    schedule 20.10.2016

Если я правильно понимаю, вы можете использовать другой способ анализа ваших данных:

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

Таким образом вы можете найти конкретный элемент из массива сущностей по индексам или в начале синтаксического анализа получить корневой элемент (элемент, который не содержит своего родителя в массиве элементов)

person VVildVVolf    schedule 20.10.2016