递归数据结构将每个分支分开

时间:2015-11-05 23:15:45

标签: javascript arrays json recursion

我有一个递归数据结构,如下面的例子,我希望它可以是每个分支的目标,从null(parentTagId)扩展到最后一个。

我不知道怎么做,任何建议都会受到赞赏!!

原始数据:

[ 
  { TagId: 2, ParentTagId: null, Name: 'women' },
  { TagId: 5, ParentTagId: 2, Name: 'bottom' },
  { TagId: 4, ParentTagId: 2, Name: 'top' },
  { TagId: 7, ParentTagId: 4, Name: 'shirt' },
  { TagId: 8, ParentTagId: 4, Name: 'tshirt' },
  { TagId: 12, ParentTagId: 7, Name: 'longsleeve' },
  { TagId: 16, ParentTagId: null, Name: 'men' }
]

预期结果:

women > bottom  
women > top > shirt > longsleeve   
women > tshirt  
men  

输出数据:

[
  {
    path: [ 
      { TagId: 2, ParentTagId: null, Name: 'women' },
      { TagId: 5, ParentTagId: 2, Name: 'bottom' }
    ]
  },
  {
    path: [
      { TagId: 2, ParentTagId: null, Name: 'women' },
      { TagId: 4, ParentTagId: 2, Name: 'top' },
      { TagId: 7, ParentTagId: 4, Name: 'shirt' },
      { TagId: 12, ParentTagId: 7, Name: 'longsleeve' }
    ]
  },
  {
    path: [
      { TagId: 2, ParentTagId: null, Name: 'women' },
      { TagId: 4, ParentTagId: 2, Name: 'top' },
      { TagId: 8, ParentTagId: 4, Name: 'tshirt' }
    ]
  },
  {
    path: [
      { TagId: 16, ParentTagId: null, Name: 'men' }
    ]
  }
]

3 个答案:

答案 0 :(得分:2)

将输入数据视为树。您想为每个生成path。 leaf是带有TagId的标记,未被任何其他标记引用为ParentTagId

所以最简单的解决方案是:

  1. 遍历所有标记并构建所有ParentTagId值的集合(即具有唯一条目的列表)。对于您的数据,即[2,4,7]
  2. 通过迭代所有标记并选择那些TagId不在该集合中的标记来查找您的树叶。对于您的数据,即[5,8,12,16]
  3. 编写一个函数getTagById以通过其ID检索标记。
  4. 编写递归函数以生成路径。
  5. 迭代所有树叶并将getPath([], leaf)的结果推送到数组paths。之后,paths包含每个叶子的路径作为标记数组。
  6. 根据paths
  7. 构建输出

    第1步的代码:

    var parentTagIdSet = [];
    for (var i = 0; i < originData.length; ++i) {
      var parentTagId = originData[i].ParentTagId;
      if (parentTagId != null && parentTagIdSet.indexOf(parentTagId) == -1) {
        parentTagIdSet.push(parentTagId);
      }
    }
    

    第2步的代码:

    var leaves = [];
    for (var i = 0; i < originData.length; ++i) {
      var tag = originData[i];
      if (parentTagIdSet.indexOf(tag.TagId) == -1) {
        leaves.push(tag);
      }
    }
    

    第3步的代码:

    function getTagById(id) {
      for (var i = 0; i < originData.length; ++i) {
        var tag = originData[i];
        if (tag.TagId == id) {
          return tag;
        }
      }
      // If you finish the loop without returning, a ParentTagId is wrong.
      return null;
    }
    

    第4步的代码:

    function getPath(path, currentTag) {
       if (currentTag == null) {
         // If you end up in here, some ParentTagId was wrong.
         path.reverse();
         return path;
       }
       path.push(currentTag);
       var parentId = currentTag.ParentTagId;
       if (parentId == null) {
         path.reverse();
         return path;
       } else {
         return getPath(path, getTagById(parentId));
       }
     }
    

    第5步的代码:

    var paths = [];
    for (var i = 0; i < leaves.length; ++i) {
      paths.push(getPath([], leaves[i]));
    }
    

答案 1 :(得分:0)

你基本上想要一个具有以下结构的对象......

class Trie implements IteratorAggregate
{
    protected $parent;
    protected $children = [];

    public function insert(Node $node)
    {
        $node->parent     = $this;
        $this->children[] = $node;
    }

    public function findById($id)
    {
        foreach($this->children as $childNode) {
            if ($childNode->TagId === $id) {
                return $childNode;
            }
            if ($childNode->hasChildren()) {
                $n = $childNode->findById($id);
                if ($n) {
                   return $n;
                }
            }
        }
    }

    public function hasChildren()
    {
        return (bool) count($this->children);
    }

    public function getIterator()
    {
        return new ArrayIterator($this->children);
    }
}

和节点......

class Node extends Trie
{
    public function __construct(stdClass $obj)
    {
        foreach($obj as $p => $v) {
            $this->$p = $v;
        }
    }
}

您可以遍历对象列表并实例化每个节点,然后将父id为null的对象插入到树的根中,并将具有子节点的节点直接插入其节点。

e.g。

$list = json_decode(<<<'JSON'
[ 
  { "TagId": 2, "ParentTagId": null, "Name": "women" },
  { "TagId": 5, "ParentTagId": 2, "Name": "bottom" },
  { "TagId": 4, "ParentTagId": 2, "Name": "top" },
  { "TagId": 7, "ParentTagId": 4, "Name": "shirt" },
  { "TagId": 8, "ParentTagId": 4, "Name": "tshirt" },
  { "TagId": 12, "ParentTagId": 7, "Name": "longsleeve" },
  { "TagId": 16, "ParentTagId": null, "Name": "men" }
]
JSON
);

$trie = new Trie;
/* Insert all of the parentless nodes */
foreach($list as $n => $obj) {
    if (!$obj->ParentTagId) {
        $trie->insert(new Node($obj));
        unset($list[$n]);
    }
}

/* Insert all of the child nodes */
foreach($list as $n => $obj) {
    $p = $trie->findById($obj->ParentTagId);
    if ($p) {
        $p->insert(new Node($obj));
        unset($list[$n]);
    }
}

现在,当您在$trie上进行迭代时,您拥有所有父节点,然后您可以对任何子节点进行迭代,以深入挖掘层次结构。

答案 2 :(得分:0)

效率不高,不是很漂亮,但是JavaScript

&#13;
&#13;
    
    var input = [ 
      { TagId: 2, ParentTagId: null, Name: 'women' },
      { TagId: 5, ParentTagId: 2, Name: 'bottom' },
      { TagId: 4, ParentTagId: 2, Name: 'top' },
      { TagId: 7, ParentTagId: 4, Name: 'shirt' },
      { TagId: 8, ParentTagId: 4, Name: 'tshirt' },
      { TagId: 12, ParentTagId: 7, Name: 'longsleeve' },
      { TagId: 16, ParentTagId: null, Name: 'men' }
    ]
    
    var output = []
    var path = []
    function drill(el){
      path.push(el)
      var val = input.filter(function(e){return e.ParentTagId === el.TagId})
      if(val.length > 0)
        val.forEach(drill)
      else {
          var e = path[0]
          while(e = findParent(e))
            path.unshift(e)
          output.push({
            path: path
          })
          path = []
          return
      }
    }
    function findParent(el){
    return input.filter(function(e){return e.TagId === el.ParentTagId})[0]
    }
    input.filter(function(e){return e.ParentTagId === null}).forEach(drill)
    console.log(output)
&#13;
&#13;
&#13;