如何从树对象获取路径的子树?

时间:2017-02-16 20:38:06

标签: javascript angularjs

我有一个以下形式的树对象:

nav[role="top"] li:not(.active) a:hover  {
  height:1px;
  text-decoration: none;
  width: 70px;
  background-color: #aecacc;
  color:#fff;
}

我试图返回给定数组[0,0,0]的路径的子树,该路径将返回var data = [{ "root": true, "label": null, "question": "What product category?", "nodes": [{ "label": "Clothes", "question": "What type of clothing?", "nodes": [{ "label": "Jeans", "question": "Color of jeans?", "nodes": [{ "label": "Blue", "question": null, "nodes": null }, ] }] }] }]; 。我正在尝试一种迭代方法,是否有可能以递归方式进行?

1 个答案:

答案 0 :(得分:4)

这是递归函数:

function subTree(arr, indexes) {
    var a = arr[indexes[0]];
    return indexes.length > 1 ? subTree(a.nodes, indexes.slice(1)) : a;
}

var data = [{
    "root": true,
    "label": null,
    "question": "What product category?",
    "nodes": [{
        "label": "Clothes",
        "question": "What type of clothing?",
        "nodes": [{
            "label": "Jeans",
            "question": "Color of jeans?",
            "nodes": [{
                "label": "Blue",
                "question": null,
                "nodes": null
              }]  
        }]
    }]
}];

console.log(subTree(data, [0,0,0]));
.as-console-wrapper { max-height: 100% !important; top: 0; }

达到此解决方案的思考过程

正如您在评论中提出的问题,这是我在解决这个问题时所经历的过程:

我想到的第一件事:我可能需要通过对象属性来找到有孩子的那个。

第二件事:不,我没有 - 它总是nodes属性。

然后:但是等待 - 第一个索引不是来自nodes属性,而是所有其他索引。获得通用解决方案是一个问题吗?

然后,我意识到它不是,因为第一次调用以数组开始 - 我不需要知道它是一个作为nodes属性的值给出的数组还是只是初始数据数组。数组是一个数组。只有在第一次递归调用时(以及所有更深层次的调用),我才需要按名称提及nodes属性,并且它始终是该属性名称。

似乎合乎逻辑的是,在每次递归时,您都会传递nodes属性的数组和剩余索引的数组,因此没有已经处理过的索引。后一个数组将减少为一个空数组,这将是递归的终点。

然后我开始编写代码来处理递归结束的情况,即你没有索引的地方。首先我想写

function subTree(arr, indexes) {
    if (!indexes.length) return arr;
    // ...
}

但这不对,因为返回值不应该是一个数组,而是具有该数组的对象 - 所以实际上这个代码会被递归得太深了!

所以第二次尝试成了:

function subTree(arr, indexes) {
    var index = indexes.shift();
    if (indexes.length == 0) return arr[index];
    // ...
}

shift调用似乎是删除第一个索引并将其保存在单独变量中的好方法,因此它可以用于返回正确的对象。

这看起来很好:如果调用是subTree(data, [0]),我的确会得到理想的结果。

然后我继续编写递归案例:

function subTree(arr, indexes) {
    var index = indexes.shift();
    if (indexes.length == 0) return arr[index];
    return subTree(arr[index].nodes, indexes);
}

这看起来也很好。它奏效了。

但是看到两个由return分隔的if语句需要三元运算符,所以我将其更改为:

function subTree(arr, indexes) {
    var index = indexes.shift();
    return indexes.length ? subTree(arr[index].nodes, indexes) : arr[index];
}

我发布了这个,然后意识到使用shift不是一个好主意:它修改了传递给函数的参数,这对调用者来说不是很好:它是一个意想不到的副作用。所以我应该创建新的数组实例,而不是修改给定的索引数组:

function subTree(arr, indexes) {
    var index = indexes[0];
    return indexes.length > 1 ? subTree(arr[index].nodes, indexes.slice(1)) : arr[index];
}

slice是一个获取给定数组的一部分的浅表副本的方法,因此仅使用1参数创建一个新数组,它不再具有第一个索引。通过这种工作方式,条件当然可以从简单的indexes.length更改为indexes.length > 1

最后,我觉得有点愚蠢,我创建了一个变量index,避免重复[0],但没有避免两次使用arr[index]。那么最后的解决方案就浮出水面了。

希望这有帮助。