简体   繁体   中英

How to convert a 2D array to a tree (of objects) in JavaScript?

I have a 2D array representing a tree in this format:

[["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]]

Each item (node) is an array where its first element is the name, and the second is the level (depth) of the node.

I need to convert this 2D array to nested JavaScript objects, where each node-object consists of name (string) and children (array of objects). The result should be the root object (here, "Food") with all other items as its children. The input array will always come ordered, so it can be assumed that the first element is root.

What's the best way to go about this, either with iteration or recursion?

Recursion isn't necessary. I think something like this is what you're looking for:

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

Here's a mostly pointless (and less efficient) recursive solution:

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

(Note that this relies on destructuring assignment to skip elements that have already been processed. You could remove this feature, but it would be much slower.)

I call this mostly pointless because it's essentially trying to imitate iteration in the way it maintains a count of elements that it's already processed, so that it can skip ahead in the list. I don't say it's completely useless because I still find the recursive version easier to read than its iterative counterpart.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM