Мои данные имеют следующие свойства:
- Каждая запись имеет уникальный идентификатор (Id)
- У каждого есть родительское поле, которое указывает на Id родителя.
- Узел может иметь несколько дочерних элементов, но только один родительский элемент.
Моя первая попытка построить дерево ниже. Это глючит, поскольку рекурсия вызывает бесконечный цикл. Даже если я решу это, я не уверен, есть ли лучший подход для этого. В настоящее время я делаю это в 2 прохода.
Я хотел бы, чтобы он был максимально эффективным, поскольку у меня есть приличный объем данных. Также необходимо динамически перестроить дерево (корнем может быть любой узел)
Ниже приведены примеры данных в программе:
arry = [{"Id":"1", "Name":"abc", "Parent":""}, {"Id":"2", "Name":"abc", "Parent":"1"},
{"Id":"3", "Name":"abc", "Parent":"2"},{"Id":"4", "Name":"abc", "Parent":"2"}]//for testing
Я надеялся, что выходные данные будут (это может быть неправильная вложенная структура, как я ее написал вручную. Но я надеюсь, что это допустимая структура JSON с узлом в качестве поля "значение" и дочерними в виде массива.)
{
"value": {"Id":"1", "Name":"abc", "Parent":""},
"children": [
{
"value": {"Id":"2", "Name":"abc", "Parent":"1"},
"children": [
{
"value": {"Id":"3", "Name":"abc", "Parent":"2"},
"children": []
},
{
"value": {"Id":"4", "Name":"abc", "Parent":"2"},
"children": []
}
]
..
}
Пример программы:
function convertToHierarchy(arry, root)
{
//root can be treated a special case, as the id is known
arry = [{"Id":"1", "Name":"abc", "Parent":""}, {"Id":"2", "Name":"abc", "Parent":"1"},
{"Id":"3", "Name":"abc", "Parent":"2"},{"Id":"4", "Name":"abc", "Parent":"2"}]//for testing
var mapping = {}; // parent : [children]
for (var i = 0; i < array.length; i++)
{
var node = arry[i];
if (!mapping[node.Id]) {
mapping[node.Id] = {value: node, children:[] } ;
}else{
mapping[node.Id] = {value: node} //children is already set
}
if (!mapping[node.Parent]) { //TODO what if parent does not exist.
mapping[node.Parent] = {value: undefined, children:[ {value: node,children:[]} ]};
}else {//parent is already in the list
mapping[node.Parent].children.push({value: node,children:[]} )
}
}
//by now we will have an index with all nodes and their children.
//Now, recursively add children for root element.
var root = mapping[1] //hardcoded for testing, but a function argument
recurse(root, root, mapping)
console.log(root)
//json dump
}
function recurse(root, node, mapping)
{
var nodeChildren = mapping[node.value.Id].children;
root.children.push({value:node.value, children:nodeChildren})
for (var i = 0; i < nodeChildren.length; i++) {
recurse(root, nodeChildren[i], mapping);
}
return root;
}
Пока у меня есть 3 хороших решения, и надеюсь, что положительные отзывы предложат более идиоматическую и эффективную реализацию. Я не уверен, используя свойство моих данных, что в наборе входного массива будет только один корневой элемент, а также всегда задан корневой элемент, любая из этих реализаций может быть лучше. Я также должен научиться тестировать, так как мое требование - насколько эффективно (быстро/без большого количества памяти) дерево можно перестроить. Например, входные данные уже кэшированы (массив) и перестроить дерево, как
convertToHierarchy(parentid)
....
convertToHierarchy(parentid2)
...