Javascript:找到树中元素的所有父母

时间:2017-09-26 11:52:00

标签: javascript recursion tree

我有对象树,我找不到具体对象id的所有父对象。想象一下,我需要为id = 5的对象的每个父项添加一些新字段。有人可以通过树的递归循环来帮助吗

var tree = {
  id: 1,
  children: [
  	{
		id: 3,
		parentId: 1,
		children: [
		  	{
				id: 5,
				parentId: 3,
				children: []
			}
		]
	}
  ]
}

console.log(searchTree (tree, 5));

function searchTree (tree, nodeId){
      for (let i = 0; i < tree.length; i++){
        if (tree[i].id == nodeId) {
            // it's parent
            console.log(tree[i].id);
            tree[i].newField = true;
            if (tree[i].parentId != null) {
              searchTree(tree, tree[i].parentId);
            }
        }
      }
 }

4 个答案:

答案 0 :(得分:4)

数据构建器

人们需要停止写这样的数据:

const tree = 
  { id: 1, parentId: null, children:
    [ { id: 3, parentId: 1, children:
      [ { id: 5, parentId: 3, children: [] } ] } ] }

并使用数据构造函数

开始编写数据

&#13;
&#13;
// "Node" data constructor
const Node = (id, parentId = null, children = Children ()) =>
  ({ id, parentId, children })

// "Children" data constructor
const Children = (...values) =>
  values

// write compound data
const tree =
  Node (1, null, 
    Children (Node (3, 1,
      Children (Node (5, 3)))))

console.log (tree)
// { id: 1, parentId: null, children: [ { id: 3, parentId: 1, children: [ { id: 5, parentId: 3, children: [] } ] } ] }
&#13;
&#13;
&#13;

这样,您就可以将注意力与详细信息区分开来,例如{}[]甚至x => ...是否用于包含您的数据。我会更进一步,创建一个带有保证tag字段的统一接口 - 以便以后可以将其与其他通用数据区分开来

完美的堆栈片段会在下面的程序中对输出进行处理。 无关紧要 打印出来时的数据是什么 - 重要的是它对我们人类来说很容易读/写在我们的程序中,我们的程序很容易读取 /

当/如果您需要特定格式/形状时,将其强制转换为然后;直到那一点,保持它很好,易于使用

&#13;
&#13;
const Node = (id, parentId = null, children = Children ()) =>
  ({ tag: Node, id, parentId, children })

const Children = (...values) =>
  ({ tag: Children, values })

// write compound data
const tree =
  Node (1, null, 
    Children (Node (3, 1,
      Children (Node (5, 3)))))

console.log (tree)
// { ... really ugly output, but who cares !.. }
&#13;
&#13;
&#13;

让我们进行搜索

我们可以使用简单的search辅助函数来编写loop - 但请注意您看到的内容;几乎没有逻辑(使用单个三元表达式);没有像for / while这样的命令式构造或像i++这样的手动迭代器递增;不使用push / unshift等变体或.forEach等有效函数;使用.length样式查找没有对[i]属性或直接索引读取的无意义检查 - 它只是函数和调用;我们不必担心任何其他噪音

&#13;
&#13;
const Node = (id, parentId = null, children = Children ()) =>
  ({ tag: Node, id, parentId, children })

const Children = (...values) =>
  ({ tag: Children, values })

const tree =
  Node (1, null, 
    Children (Node (3, 1,
      Children (Node (5, 3)))))

const search = (id, tree = null) =>
  {
    const loop = (path, node) =>
      node.id === id
        ? [path]
        : node.children.values.reduce ((acc, child) =>
            acc.concat (loop ([...path, node], child)), [])
    return loop ([], tree)
  }

const paths =
  search (5, tree) 

console.log (paths.map (path => path.map (node => node.id)))
// [ 1, 3 ]
&#13;
&#13;
&#13;

所以search返回路径的数组,其中每个路径都是节点的数组 - 为什么会这样呢?如果ID为X的儿童出现在树中的多个位置,则将返回所有路径

&#13;
&#13;
const Node = (id, parentId = null, children = Children ()) =>
  ({ tag: Node, id, parentId, children })

const Children = (...values) =>
  ({ tag: Children, values })

const tree =
  Node (0, null, Children (
    Node (1, 0, Children (Node (4, 1))),
    Node (2, 0, Children (Node (4, 2))),
    Node (3, 0, Children (Node (4, 3)))))

const search = (id, tree = null) =>
  {
    const loop = (path, node) =>
      node.id === id
        ? [path]
        : node.children.values.reduce ((acc, child) =>
            acc.concat (loop ([...path, node], child)), [])
    return loop ([], tree)
  }
  
const paths =
  search (4, tree) 

console.log (paths.map (path => path.map (node => node.id)))
// [ [ 0, 1 ],
//   [ 0, 2 ],
//   [ 0, 3 ] ]
&#13;
&#13;
&#13;

你不小心写了monad列表

列表monad编码模糊计算的想法 - 也就是说,可以返回一个或多个结果的计算的想法。让我们对我们的程序做一个小改动 - 这是有利的,因为List是通用的,现在可以在我们的程序中的其他地方使用,这种计算是必不可少的

如果你喜欢这个解决方案,你可能会喜欢阅读my other answers that talk about the list monad

&#13;
&#13;
const List = (xs = []) =>
  ({
    tag:
      List,
    value:
      xs,
    chain: f =>
      List (xs.reduce ((acc, x) =>
        acc.concat (f (x) .value), []))
  })

const Node = (id, parentId = null, children = Children ()) =>
  ({ tag: Node, id, parentId, children })

const Children = (...values) =>
  List (values)

const search = (id, tree = null) =>
  {
    const loop = (path, node) =>
      node.id === id
        ? List ([path])
        : node.children.chain (child =>
            loop ([...path, node], child))
    return loop ([], tree) .value
  }
  
const tree =
  Node (0, null, Children (
    Node (1, 0, Children (Node (4, 1))),
    Node (2, 0, Children (Node (4, 2))),
    Node (3, 0, Children (Node (4, 3)))))

const paths =
  search (4, tree) 

console.log (paths.map (path => path.map (node => node.id)))
// [ [ 0, 1 ],
//   [ 0, 2 ],
//   [ 0, 3 ] ]
&#13;
&#13;
&#13;

答案 1 :(得分:2)

最简单的解决方案是将树形结构向下展平,这样您就可以查找ID并执行简单的while循环

var tree = {
  id: 1,
  children: [
  	{
		id: 3,
		parentId: 1,
		children: [
		  	{
				id: 5,
				parentId: 3,
				children: []
			}
		]
	}
  ]
}

// We will flatten it down to an object that just holds the id with the object
var lookup = {}
function mapIt (node) {
  lookup[node.id] = node;
  //recursive on all the children
  node.children && node.children.forEach(mapIt);
}
mapIt(tree)

// This takes a node and loops over the lookup hash to get all of the ancestors
function findAncestors (nodeId) {
   var ancestors = []
   var parentId = lookup[nodeId] && lookup[nodeId].parentId
   while(parentId !== undefined) {
     ancestors.unshift(parentId)
     parentId = lookup[parentId] && lookup[parentId].parentId
   }
   return ancestors;
}

// Let us see if it works
console.log("5: ",  findAncestors(5))

答案 2 :(得分:1)

以下是工作递归函数的示例。

玩了一会儿,你应该是金色的

&#13;
&#13;
var tree = {
  id: 1,
  children: [{
    id: 3,
    parentId: 1,
    children: [{
      id: 5,
      parentId: 3,
      children: []
    }]
  }]
}

function mapit(node, parent = null) {
  node.parent = parent;
  if (node.children.length > 0) {
    for (var i = 0; i < node.children.length; i++) {
      var child = node.children[i];
      mapit(child, node);
    }
  }
}
mapit(tree);
console.log(tree);
&#13;
&#13;
&#13;

答案 3 :(得分:1)

递归函数并不困难。请记住,如果您的参数不符合,则将新级别传递给函数。

&#13;
&#13;
var tree = [{
  id: 1,
  children: [{
    id: 3,
    parentId: 1,
    children: [{
      id: 5,
      parentId: 3,
      children: [{
        id: 6,
        parentId: 5,
        children: [{
          id: 5,
          parentId: 3,
          children: []
        }]
      }]
    }]
  }]
}]; //wrap first obj in an array too.

searchTree(tree, 5);
console.log(tree);

function searchTree(tree, nodeId) {
  for (let i = 0; i < tree.length; i++) {
    if (tree[i].id == nodeId) {
      tree[i]; //id found, now add what you need.
      tree[i].newField = "added";
    }//if child has children of its own, continu digging.
    if (tree[i].children != null && tree[i].children.length > 0) {
      searchTree(tree[i].children, nodeId); //pass the original nodeId and if children are present pass the children array to the function.

    }
  }
}
&#13;
&#13;
&#13;