私のデータには次のプロパティがあります。
- 各エントリには一意のID(Id)があります
- それぞれに、親のIDを指すParentフィールドがあります。
- ノードは複数の子を持つことができますが、親は1つだけです。
ツリーを構築する最初の試みは以下のとおりです。再帰によって無限ループが発生するため、バグがあります。私がそれを解決したとしても、これを行うためのより良いアプローチがあるかどうかはわかりません。現在、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
私は出力がなることを望んでいました(手動で書いたので、ネストされた構造が間違っている可能性があります。しかし、私が望んでいるのは、ノードをフィールド'value'として、子を配列として持つ有効な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 doesn't 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つの優れたソリューションがあり、賛成票がより慣用的で効率的な実装を提案することを願っています。私のデータのプロパティを利用して、入力配列のセットにルート要素が1つだけあり、ルートが常に指定されているかどうかはわかりません。これらの実装のいずれかがより優れている可能性があります。私の要件はツリーをどれだけ効率的に(高速/多くのメモリなしで)再構築できるかということなので、ベンチマークの方法も学ぶ必要があります。たとえば、入力はすでにキャッシュされており(配列)、次のようにツリーを再構築します。
convertToHierarchy(parentid)
....
convertToHierarchy(parentid2)
...