简体   繁体   中英

Is Ruby turning my hash into an Array, if so why?

So I'm trying to make a tree data structure, which can be instantiated by a nested hash.

My code is as follows, and should just recursively make nodes out of keys, and children out of their values.

class Tree
    attr_accessor :children, :node_name
    #def initialize(name, children=[])
    #   @children = children
    #   @node_name = name
    # end
    def initialize(hashTree)
        @node_name = hashTree.keys[0]
        @children = []
        p node_name
        hashTree[node_name].each do |hash|
            children << Tree.new(hash)
        end
    end
    #...
end

p = {'grandpa' => { 'dad' => {'child 1' => {}, 'child2' => {} }, 'uncle' => {'child 3' => {}, 'child 4' => {} } } }
p p
p Tree.new(p)

When I try to run that code I get the following:

{"grandpa"=>{"dad"=>{"child 1"=>{}, "child2"=>{}}, "uncle"=>{"child 3"=>{}, "child 4"=>{}}}}
"grandpa"
/Users/Matt/sw/sevenLang/week1/hw-tree.rb:8:in `initialize': undefined method `keys' for ["dad", {"child 1"=>{}, "child2"=>{}}]:Array (NoMethodError)
    from /Users/Matt/sw/sevenLang/week1/hw-tree.rb:12:in `new'
    from /Users/Matt/sw/sevenLang/week1/hw-tree.rb:12:in `block in initialize'
    from /Users/Matt/sw/sevenLang/week1/hw-tree.rb:11:in `each'
    from /Users/Matt/sw/sevenLang/week1/hw-tree.rb:11:in `initialize'
    from /Users/Matt/sw/sevenLang/week1/hw-tree.rb:26:in `new'
    from /Users/Matt/sw/sevenLang/week1/hw-tree.rb:26:in `<main>'
[Finished in 0.1s with exit code 1]

It looks like each is turning the nested hash into an array, where the key is the first element, and the value is the second element.

hashTree[node_name] is p["grandpa"] , and is a Hash:

{"dad"=>{"child 1"=>{}, "child2"=>{}}, "uncle"=>{"child 3"=>{}, "child 4"=>{}}}

Hash#each will yield a two-element array: a key and a value. So if you write

hashTree[node_name].each do |hash|

and hashTree[node_name] is a Hash, hash will always be a two-element array. Due to a trick in its grammar, Ruby will auto-splat an array argument if there are multiple formal parameters, so you can also write:

hashTree[node_name].each do |name, hash|

This will not result in an error. (You do actually still have an unrelated error in logic, as you're skipping a level.)

An error-free version:

class Tree
    attr_accessor :children, :node_name
    def initialize(name, hashTree)
        @node_name = name
        @children = []
        hashTree.each do |child_name, hash|
            children << Tree.new(child_name, hash)
        end
    end
end

p = {'grandpa' => { 'dad' => {'child 1' => {}, 'child2' => {} }, 'uncle' => {'child 3' => {}, 'child 4' => {} } } }
p Tree.new("Family", p)

This can be shortened by using map :

class Tree
    attr_accessor :children, :node_name
    def initialize(name, hashTree)
        @node_name = name
        @children = hashTree.map do |child_name, hash|
            Tree.new(child_name, hash)
        end
    end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM