计算通过树的路径以获得匹配算法

时间:2015-11-03 18:44:10

标签: javascript recursion

我正在尝试开发一个函数来搜索树状结构并返回通过简单字符串测试(indexOf)的所有节点的完整路径。 我认为我有点接近,因为我可以处理任意深度的N个根节点,只要路径是线性的。当我分支时会出现问题,因为应该发生的是每个分支变成包含其整个路径的不同数组。但是,正在发生的事情是每个分支被合并到一个公共数组中,该数组表示发生分支的父级所有子级下面的匹配路径。

http://plnkr.co/edit/tbH0FW6T11F3iwNIBHdN?p=preview

简单搜索显示正确的结果,数据中没有分支

搜索显示数据中分支的错误/合并结果

在上面的图像中,结果应该包含三个元素,每个元素代表遍历的不同路径(字符串测试匹配'Z')。 [1AA,1A,1],[1aba,1AA,1A,1],[2AA,2A,2]

注释数据中的“Zoe”条目,以便在线性和分支测试之间切换。

(function(){
  
  var data = 
  [
    {"name" : "Mike" , "id" : "1","children" : 
    [
      {"name" : "Jim" , "id" : "1a", "children" : 
      [
        {"name" : "Zoe" , "id" : "1aa", "children":[]}, //Uncomment for fail test
        {"name" : "Carrie" , "id" : "1ab", "children" :
        [
          {"name" : "Zane" , "id" : "1aba", "children":[]}
        ]}
      ]}
    ]},
    {"name" : "Allen" , "id" : "2","children" : 
    [
      {"name" : "Fred" , "id" : "2a", "children" : 
      [
        {"name" : "Zach" , "id" : "2aa", "children" :
        [
          {"name" : "Dean" , "id" : "2aaa", "children":[]}
        ]}
      ]}
    ]}
  ];
  
  var rFX = function(val,item){
    
    var subMatches =[];

    subMatches = (item.children.map(function(child){
      return rFX(val,child);
    }));
    
    subMatches = [].concat.apply([], subMatches);
    
    if(item.name.indexOf(val) > -1 || subMatches.length >0){
      subMatches.push(item.id);
    }
    
    return subMatches;
    
  };
  
  var result = data.map(function(item){
    return rFX("Z",item);
  });
  
  console.log(result);
 
})();

1 个答案:

答案 0 :(得分:0)

我放弃了我的初始方法作为合并数据的复杂性,因为递归调用返回堆栈似乎太难了。

相反,我将问题分解为两个功能。

  1. getAllPaths:此函数遍历整个结构,计算到叶节点的所有可能路径的数组。这里不执行过滤。每个递归调用都传递一个先前在其路径中处理的节点数组。当函数到达一个没有子节点的节点时,它会添加数组的最终路径,然后将路径数组放在一个主数组上,所有递归调用都已关闭。

  2. filterPaths:此函数会从getAllPaths返回的集合中抄写每个路径(自下而上),直到找到匹配项,从而为我们提供匹配项目的路径集合。

  3. http://plnkr.co/edit/tbH0FW6T11F3iwNIBHdN

    (function() {
    
      var data = [{
        "name": "Mike",
        "id": "1",
        "children": [{
          "name": "Jim",
          "id": "1a",
          "children": [{
              "name": "Zoe",
              "id": "1aa",
              "children": []
            },
            {
              "name": "Carrie",
              "id": "1ab",
              "children": [{
                "name": "Zane",
                "id": "1aba",
                "children": []
              }]
            }
          ]
        }]
      }, {
        "name": "Allen",
        "id": "2",
        "children": [{
          "name": "Fred",
          "id": "2a",
          "children": [{
            "name": "Zach",
            "id": "2aa",
            "children": [{
              "name": "Dean",
              "id": "2aaa",
              "children": []
            }]
          }]
        }]
      }];
    
      //Return all complete paths in the data (paths to leaf nodes)
      var getAllPaths = function(items) {
    
        //Array which all calls will push their completed path arrays onto.
        var master = [];
    
        //Recursive call to travese every path in our data.
        var rFXi = function(item, itemArray) {
    
          itemArray.push(item); //always push current item
    
          //If we still have children process them recursively
          if (item.children && item.children.length > 0) {
            item.children.forEach(function(child) {
              rFXi(child, itemArray.slice());
            });
          }
    
          //else we are at a leaf node so push complete path array onto master.
          else {
            master.push(itemArray);
          }
    
        };
    
        //boot up our recursive calls
        items.forEach(function(child) {
          rFXi(child, []);
        });
    
        return master;
      };
    
      //Test our paths against a comparator function and,
      //remove all path segments beneath a terminal match
      //and then return only those paths remaining with a
      //length greater than zero.
      var filterPaths = function(paths, comparator) {
    
        paths.forEach(function(path) {
    
          var matched = false,
            length = path.length,
            i;
    
          for (i = path.length - 1; i >= 0 && !matched; i--) {
            if (comparator(path[i], "D")) {
              matched = true;
            } else {
              //remove non matching leaf
              path.splice(i, 1);
            }
          }
    
        }); //forEach end
    
        return paths.filter(function(path) {
          return path.length > 0;
        });
    
      };
    
      //Retrieve all paths to leaf nodes in our "tree"
      var paths = getAllPaths(data);
      console.log(paths);
    
      //Now filter those paths for matching segments
      paths = filterPaths(paths, function(item, val) {
        return item.name.indexOf(val) > -1;
      });
      console.log(paths);
    
    
    })();