small medium large xlarge

Generic-user-small
26 Sep 2010, 00:02
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.

Generic-user-small
02 Oct 2010, 21:53
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

Generic-user-small
13 Nov 2010, 21:24
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

Generic-user-small
24 Nov 2010, 02:26
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
Generic-user-small
30 Nov 2010, 00:20
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
Photo_on_2011-12-31_at_08.53_pragsmall
15 Dec 2010, 02:03
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.

Pat_glasses_pragsmall
21 Dec 2010, 16:51
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?

Generic-user-small
29 Dec 2010, 12:32
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
Generic-user-small
15 Feb 2011, 11:55
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
Generic-user-small
30 Mar 2011, 21:08
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'=>{} } })
Avatar_orig_180x166_pragsmall
08 Nov 2011, 20:40
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
Generic-user-small
04 Apr 2012, 16:16
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
Generic-user-small
13 Jun 2012, 13:22
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
Generic-user-small
21 Oct 2012, 17:37
Pierre Sugar (56 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
Generic-user-small
27 Jan 2015, 08:08
Brunoni Yar (1 post)

That’s the 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