26 Sep 2010, 00:02
Generic-user-small

mozart reina (1 post)

i’m having trouble implementing the tree with nested hashes, partly because i don’t really understand the structure and syntax of ruby well. can anyone provide a solution for me to reverse engineer? doesn’t need any comments, i just need to see how it’s done… i’ve spent the last two days trying to figure it out, i’ve done the other exercises.

02 Oct 2010, 21:53
Generic-user-small

Finn Newick (1 post)

I found this bit tricky too, after a little fiddling around I came up with this which is pretty simple:

class Tree
  attr_accessor :children, :node_name
   
  def initialize(hash, name=nil)
    if name.nil?
        arr = hash.to_a
        init_tree(arr[0][1],arr[0][0])
    else
        init_tree(hash, name)
    end
  end

  def init_tree(hash, name)
    @node_name = name
    @children = hash.inject([]) do |arr, (a, b)|
        arr.push(Tree.new(b,a))
    end
  end

  def visit_all(&block)
    visit &block
    children.each {|c| c.visit_all &block}
  end

  def visit(&block)
    block.call self
  end
end

This is the first bit of ruby I’ve ever written so no idea if it’s in a ‘rubonic’ (if there is such a word!) style, or what was intended in the exercise (I flipped the constructor arguments around, and my gut feeling is that that is unnecessary).

Have fun Finn

13 Nov 2010, 21:24
Generic-user-small

Geert Guldentops (1 post)

I have a slightly shorter implementation (but not necessarily better) of this problem but I am really curious what other ‘novices’ came up with:

class Tree
  attr_accessor :children, :node_name
  
  def initialize(tree={})  
    tree.each_pair do |node_name, children| 
      node_children = []
    
      children.each_pair do |child_name, grandchildren|      
        node_children.push(Tree.new({child_name => grandchildren}))
      end
      
      @node_name = node_name
      @children = node_children
    end
  end
  
  def visit_all(&block)
    visit &block
    children.each {|child| child.visit_all &block}
  end
  
  def visit(&block)
    block.call self
  end
end
                  
my_tree = Tree.new( 
  {'grandpa' => { 'dad' => {'child 1' => {}, 'child 2' => {}}, 
                  'uncle' => {'child 3' => {}, 'child 4' => {}}
                }
  }
)
                  
puts "Visiting a node"
my_tree.visit {|node| puts node.node_name}
puts

puts "Visiting entire tree"
my_tree.visit_all {|node| puts node.node_name}

I found the question a bit strange since it said ‘Let the initializer accept a nested structure with hashes and arrays.’ when only nested hashes are passed to the tree.

Kind regards,

Geert

24 Nov 2010, 02:26
Generic-user-small

Horacio Gonzalez (1 post)

I have another solution, that is more similar in spirit to the first one. Maybe it’s not the prettiest Ruby, but does the trick with only 4 lines of initialization:

  def initialize(node)
        @node_name = node.to_a[0][0]
        c = node.to_a[0][1]
        @children = []
        c.each {|k,v| @children.push Tree.new({k, v})}
  end
30 Nov 2010, 00:20
Generic-user-small

Roger Roelofs (1 post)

I also had to think pretty hard to get this one. I’m not sure it is fluent, but it did turn out short.


  def initialize(data)
    data.each do |k, v|
      @node_name = k
      @children = v.inject([]) {|a, (k, v)| a.push(Tree.new({k => v}))}
    end
  end
15 Dec 2010, 02:03
Photo_on_2011-12-31_at_08.53_pragsmall

Michael Koziarski (3 posts)

  def initialize(data)
    data.each do |k, v|
      @node_name = k
      @children = v.map {|(k, v)| Tree.new(k => v)}
    end
  end

</code>

Would possibly be a little more idiomatic.

21 Dec 2010, 16:51
Pat_glasses_pragsmall

Patrick Shields (1 post)

Is the choice of each instead of map purposeful? Since we only care about the side effects, why would one choose one over the other?

29 Dec 2010, 12:32
Generic-user-small

Vilius Normantas (1 post)

I made an assumption that the top level hash has only one key, so I wrote it this way:

  def initialize(hash)
    @node_name = hash.keys.at(0)
    @children = hash[node_name].to_a.map { |(k, v)| 
      Tree.new({k => v}) 
    }
  end
15 Feb 2011, 11:55
Generic-user-small

Cristofer Weber (1 post)

I’ve tried a recursive solution, but it appears that when at the leaf node level (maybe because of the existance of more than one element) Ruby considers as an Array, not as a Hash. Any idea?

    def Tree.build_from_hash(elements={})

        elements.each do |parent, sons|
           childrens = sons.inject([]) { |array, son| array << build_from_hash(son) } unless sons.nil?
           childrens ||= []
           return new(parent, childrens)
        end
    end
30 Mar 2011, 21:08
Generic-user-small

Ton van Bart (1 post)

I wanted a recursive solution as well but also wanted to guard against an input hash with more than one key at the root level. Eventually came up with this solution which uses a class method to kick things off (I don’t know if I should consider this cheating):

class Tree 
    attr_accessor :children, :node_name
    
    def self.create(data={})
        root = Tree.new("root",data)
    end
    
    def initialize(name, childhash={})
        @node_name = name
        @children = []
        childhash.each { |aname,ahash| @children.push(Tree.new(aname,ahash)) }
    end
    
    def visit_all(&block)
        children.each {|c| c.visit_all &block}
        visit &block
    end
    
    def visit(&block)
        block.call(self)
    end
end

rt = Tree.create({'Ruby' => {'Reia'=>{}, 'MacRuby'=>{} } })
08 Nov 2011, 20:40
Avatar_orig_180x166_pragsmall

Martin Mares (1 post)

This is my version of the code. Perhaps someone will be helpful.

class Tree
  attr_accessor :node_name, :deep, :childrens

  def initialize(node_name, deep=0, childrens={})
    @node_name = node_name
    @deep = deep
    @childrens = childrens
  end

  def visit_all(&b)
    visit &b
    childrens.each do |node_name, childs|
      t = Tree.new(node_name, deep+2, childs)
      t.visit_all &b
    end
    nil
  end

  def visit(&b)
    b.call self
  end

end

t = Tree.new("Root", 0, {
    'grandpa' => {
          'dad' => {
              'child 1' => {},
              'child 2' => {}
          },
          'uncle' => {
              'child 3' => {},
              'child 4' => {} }
    }
  }
)

puts
puts "== Visiting node"
puts t.visit { |node, deep| puts " #{' ' * node.deep }* #{node.node_name}"  }
puts
puts "== Visiting entire tree"
puts t.visit_all { |node, deep| puts " #{' ' * node.deep }* #{node.node_name}"  }

Output is:

== Visiting node
 * Root


== Visiting entire tree
 * Root
   * grandpa
     * dad
       * child 1
       * child 2
     * uncle
       * child 3
       * child 4
04 Apr 2012, 16:16
Generic-user-small

Michael Jeppesen (1 post)

Here’s the initialize method I came up with:

  def initialize(root)
    @node_name = root.keys[0]
    @children = []
    root[node_name].entries.each{ |kid| @children << Tree.new(Hash[*kid])}
  end
13 Jun 2012, 13:22
Generic-user-small

guanxiaohua2k6 (2 posts)

Here is my answer.

  def initialize(root_node)
    @node_name, child_nodes = root_node.first
    @children = []
    child_nodes.each{|key, value| @children << Tree.new({key => value})}
  end
21 Oct 2012, 17:37
Generic-user-small

Pierre Sugar (8 posts)

and another solution

def initialize(family={})
    @children = []
    family.each do |k, v|
        @name = k
        v.each {|k, v| @children.push(Tree.new(k=>v))
    end
end
  You must be logged in to comment