如何在JavaScript中将2D数组转换为树(对象)?

时间:2011-10-25 18:49:48

标签: javascript arrays object tree

我有一个2D数组,表示这种格式的树:

[["Food", 0], ["Dairy", 1], ["Milk", 2], ["Low-fat", 3], ["Butter", 2], ["Cheese", 2], ["Vegetables", 1], ["Spinach", 2], ["Meat", 1], ["Fish", 2], ["Salmon", 3], ["Poultry", 2]]

每个项目(节点)都是一个数组,其第一个元素是名称,第二个是节点的级别(深度)。

我需要将这个2D数组转换为嵌套的JavaScript对象,其中每个节点对象由name(字符串)和children(对象数组)组成。结果应该是根对象(此处为“Food”),其他所有项目都是其子项。输入数组将始终排序,因此可以假设第一个元素是root。

通过迭代或递归,最好的方法是什么?

2 个答案:

答案 0 :(得分:2)

没有必要进行递归。我认为这就是你要找的东西:

function tree_from_list(list) {
    var list_node = list.shift();
    var depth = list_node[1];
    var root = {name: list_node[0], children: []};
    var tree_node;
    var cur_nodes = []; 
    cur_nodes[depth] = root;
    while (list.length > 0) {
        list_node = list.shift();
        tree_node = {name: list_node[0], children: []};
        depth = list_node[1];
        if (cur_nodes[depth - 1] === undefined)
            throw 'invalid list!';
        cur_nodes[depth - 1].children.push(tree_node);
        cur_nodes[depth] = tree_node;
    }
    return root;
}
var list = [["Food", 0], ["Dairy", 1], ["Milk", 2], ["Low-fat", 3], ["Butter", 2], ["Cheese", 2], ["Vegetables", 1], ["Spinach", 2], ["Meat", 1], ["Fish", 2], ["Salmon", 3], ["Poultry", 2]];
var tree = tree_from_list(list);

答案 1 :(得分:0)

这是一个无意义(效率较低)的递归解决方案:

function mktree(lis, lvl) {
    var i, el, c, itemlis = [];
    for (i = 0; el = lis[i++];) {
        if (lvl !== el[1])
            break; 
        var item = {};
        [item[el[0]], c] = mktree(lis.slice(i), lvl + 1);
        i += c - 1; // skip ahead
        itemlis.push(item);
    } 
    return [itemlis, i];
}

function lis_to_tree(arr) {
    return mktree(arr, 0, 0)[0][0];
}

var arr = [["Food", 0], ["Dairy", 1], ["Milk", 2], ["Low-fat", 3], ["2%", 3],
           ["Butter", 2], ["Cheese", 2], ["Vegetables", 1], ["Spinach", 2], 
           ["Meat", 1], ["Fish", 2], ["Salmon", 3], ["Poultry", 2]];
var tree = lis_to_tree(arr);

(请注意,这取决于解构分配以跳过已经处理过的元素。您可以删除此功能,但速度要慢得多。)

我称之为毫无意义,因为它本质上试图模仿迭代,它维护已经处理过的元素计数,因此它可以在列表中向前跳过。我并不是说完全无用,因为我仍然发现递归版本比迭代版本更容易阅读。