A7879fe675de0f2b4cc4ee4ba26585e1

Let's assume a node on a tree has a direct reference to its parent. I want to be able to traverse the entire ancestry of a node. The current solution I have reeks in my mind. Maybe I'm just overanalyzing it, but I could swear there is a more concise way.

The only given is that @parent refers to the direct parent of the node. Any changes are fair game.

class Node
  def parent
    @parent
  end

  def ancestor(generation = 0)
    node = self
    (generation + 1).times { node = node.parent }
    node
  end
end

Refactorings

No refactoring yet !

Fc98fe456f2c5a4f1c00a7a19f019cd0

Luca

June 14, 2011, June 14, 2011 11:36, permalink

No rating. Login to rate!

Finding a specific ancestor seems more difficult to me, especially if you don't know the depth of your node in the tree.
I could think to something like:

class Node

  attr_accessor :parent, :name

  def initialize(name, parent = nil)
    self.name   = name
    self.parent = parent
  end

  def ancestors(node = self)
    [].tap do |ancestors_array|
      ancestors_array << node = node.parent while node.parent
    end
  end

end

progenitor          = Node.new('progenitor')
grand_grand_parent  = Node.new('grand_grand_parent', progenitor)
grand_parent        = Node.new('grand_parent', grand_grand_parent)
parent              = Node.new('parent', grand_parent)
node                = Node.new('node', parent)

node.ancestors.map(&:name)
# => ["parent", "grand_parent", "grand_grand_parent", "progenitor"]

sparse_node = Node.new('node')

sparse_node.ancestors.map(&:name)
# => []
A8d3f35baafdaea851914b17dae9e1fc

Adam

June 14, 2011, June 14, 2011 20:50, permalink

No rating. Login to rate!

Seems like a good place to use an enumerator.

class Node
  attr_reader :parent
  
  def ancestors(&block)
    if block_given?
      if parent
        block.call(parent)
        parent.ancestors(&block)
      end
    else
      Enumerable::Enumerator.new(self, :ancestors)
    end
  end
end
D85d44a0eca045f40e5a31449277c26c

Ben Marini

June 14, 2011, June 14, 2011 21:27, permalink

No rating. Login to rate!

Here's one way

class Node < Struct.new(:name, :parent)
  def ancestors
    parent.nil? ? [] : [ parent ] + parent.ancestors
  end

  def to_s
    name
  end
end

progenitor          = Node.new('progenitor')
grand_grand_parent  = Node.new('grand_grand_parent', progenitor)
grand_parent        = Node.new('grand_parent', grand_grand_parent)
parent              = Node.new('parent', grand_parent)
node                = Node.new('node', parent)

puts node.ancestors.join(", ")

Your refactoring





Format Copy from initial code

or Cancel