【问题标题】:How do I easily retrieve the total depth of a tree in ancestry?如何轻松检索祖先树的总深度?
【发布时间】:2015-01-01 00:50:56
【问题描述】:

Ancestry 有许多很棒的作用域和方法,包括 node.depth(它返回一个深度级别 - 即根节点为 0,子节点为 1,等等)。

它还有一些范围可以让你按深度遍历树,例如node.subtree(:to_depth => 2).

但我没有看到任何指示如何计算/检索整个树的总深度 - 从根到最后一个节点。

我唯一能想到的就是在我的模型上创建一个total_depth_cache 列,当添加一个新子时,它会增加它。但是然后我必须编写逻辑来管理它(即,每当删除所有子级时,它就会递减 - 如果只有 1 级,但如果删除了具有 3 级子级的子级,则必须更新深度因此)。这很快就会变得相当复杂。

有没有更简单的方法来做到这一点?

【问题讨论】:

  • 计算孩子的数量就足够了吗? node.descendant_ids.count ?
  • 不...这不起作用...因为它包括兄弟姐妹。即如果node A 有 2 个孩子,每个孩子有 1 个孩子。 node.descendant_ids.count 将返回 4,而总深度应为 3(即根 -> 子 -> 孙),如果这有意义的话。
  • 有点臭。您可以使用total_depth_cache,但您必须通过遍历所有子代,然后是父代的父代等,在父代的 after_create 和 after_destroy 上重新计算它,然后当您看到没有变化时停止,因为父代也不会改变.如果它是一棵易变的树并且您不需要经常使用 total_depth,那么可能会更好地遍历所有子级以“根据需要”计算 total_depth。如果不检查所有其他子分支,则无法确定父级缓存值是否需要更改。
  • @SteveTurczyn 这有点像我想的......但那样有点糟糕。我可能只需要那样做。我希望有 max_depthlast_depth 方法或其他东西。这样会更容易。

标签: ruby-on-rails ruby-on-rails-4 ancestry


【解决方案1】:

这是一个last_depth 递归方法,您可以添加到模型中...

class Node
  def last_depth
    if child_ids.empty?
      return depth
    else
      return children.map{|c| c.last_depth}.max
    end
  end
end

编辑:澄清一下,如果没有子节点,该方法将返回节点的当前深度,否则它将在当前节点的所有子节点上调用自身并返回最大的结果。最终结果将是树中最大的“深度”值。

【讨论】:

  • 这感觉可能会出现“堆栈级别太深”的错误?它看起来确实很有趣。
  • 如果您未能提供基本情况(未调用方法的情况),您会在递归中得到“堆栈级别太深”。在这个例子中,当没有child_ids(没有孩子)时,它不会调用,它只是返回当前深度,所以这是基本情况,你肯定会达到基本情况,因为(最终)你会,在调用子节点时,到达没有子节点的节点。实际的堆栈级别不应大于 last_depth 计数,因此我认为堆栈溢出的可能性不大。
  • 我认为这种递归方法最终会为每个递归进行两次查询——一个用于child_ids,另一个用于children,对吗?在这方面,我发现@nahankid 的解决方案要好一些。
【解决方案2】:

有很多方法可以检索祖先树的深度。最易读的一个是:

node.subtree.select(&:is_childless?).map(&:depth).max

这会找到所有叶节点的最大深度。

【讨论】:

  • 您能否提供一些详细信息,说明为什么这可以解决 OP 的问题?
  • 这是一个比公认答案更干净的解决方案。使用 is_childless? 允许祖先 gem 产生叶节点集,这是我们在这里关心的全部。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多