以递归方式打印出散列键和值对

时间:2016-08-22 18:37:35

标签: ruby

我正在尝试定义一个函数,它可以以树格式打印出任何哈希值。该函数将执行以下操作:

来自

{"parent1"=>
    {"child1" => { "grandchild1" => 1,
                "grandchild2" => 2},
    "child2" => { "grandchild3" => 3,
                "grandchild4" => 4}}
}

   parent1:
        child1: 
              grandchild1:1
              grandchild2:2
        child2:
              grandchild3:3
              grandchild4:4

到目前为止,这是我的代码:

def readprop(foo)
    level = ''
    if foo.is_a?(Hash)
        foo.each_key {|key| if foo[key].nil? == false
                puts level + key + ":"
                level += "   "
                readprop(foo[key])
            end 
        }
    else
        puts level + foo
        level = level[0,level.length - 2]
    end
end 

它会给我一个糟糕的格式:

parent1:
child1:
grandchild1:
1
   grandchild2:
2
   child2:
grandchild3:
3
   grandchild4:
4

4 个答案:

答案 0 :(得分:4)

你快到了。解决它的一种方法是使level成为递归函数参数的一部分。 x是问题中的哈希值。

简单的递归版本:

def print_hash(h,spaces=4,level=0)
  h.each do |key,val|
    format = "#{' '*spaces*level}#{key}: "
    if val.is_a? Hash
      puts format
      print_hash(val,spaces,level+1)
    else
      puts format + val.to_s
    end
  end
end

print_hash(x)

#parent1: 
#    child1: 
#        grandchild1: 1
#        grandchild2: 2
#    child2: 
#        grandchild3: 3
#        grandchild4: 4

在这种情况下,您还可以将其转换为YAML(如上面的评论所述)

require 'YAML'
puts x.to_yaml
#---
#parent1:
#  child1:
#    grandchild1: 1
#    grandchild2: 2
#  child2:
#    grandchild3: 3
#    grandchild4: 4

答案 1 :(得分:2)

我会使用递归,但还有另一种方式可能会让一些人感兴趣。下面我使用了一台漂亮的打印机",awesome-print来完成部分格式化(特别是缩进),将结果保存到字符串中,然后应用了几个gsub's到字符串,将结果按到所需的格式。

假设您的哈希值如下:

h = { "parent1"=>
        { "child1" => { "grandchild11" => 1,
                        "grandchild12" => { "great grandchild121" => 3 } },
          "child2" => { "grandchild21" => { "great grandchild211" =>
                                           { "great great grandchild2111" => 4 } },
                        "grandchild22" => 2 }
        }
    }

然后我们可以执行以下操作。

require 'awesome_print'

puts str = h.awesome_inspect(indent: -5, index: false, plain: true).
  gsub(/^\s*(?:{|},?)\s*\n|[\"{}]/, '').
  gsub(/\s*=>\s/, ':')

打印

 parent1:
      child1:
           grandchild11:1,
           grandchild12:
                great grandchild121:3
      child2:
           grandchild21:
                great grandchild211:
                     great great grandchild2111:4
           grandchild22:2 

步骤:

str = h.awesome_inspect(indent: -5, index: false, plain: true)

puts str打印

{
     "parent1" => {
          "child1" => {
               "grandchild11" => 1,
               "grandchild12" => {
                    "great grandchild121" => 3
               }
          },
          "child2" => {
               "grandchild21" => {
                    "great grandchild211" => {
                         "great great grandchild2111" => 4
                    }
               },
               "grandchild22" => 2
          }
     }
}

s1 = str.gsub(/^\s*(?:{|},?)\s*\n|[\"{}]/, '')

puts s1打印

 parent1 => 
      child1 => 
           grandchild11 => 1,
           grandchild12 => 
                great grandchild121 => 3
      child2 => 
           grandchild21 => 
                great grandchild211 => 
                     great great grandchild2111 => 4
           grandchild22 => 2

s2 = s1.gsub(/\s*=>\s/, ':')

puts s2打印上面的结果。

答案 2 :(得分:1)

不完全符合您的要求,但我会提交此答案,因为我认为您可能认为它有用:

require 'yaml'

hash = {"parent1"=> {"child1" => { "grandchild1" => 1,"grandchild2" => 2},
                     "child2" => { "grandchild3" => 3,"grandchild4" => 4}}}

puts hash.to_yaml

打印:

---
parent1:
  child1:
    grandchild1: 1
    grandchild2: 2
  child2:
    grandchild3: 3
    grandchild4: 4

答案 3 :(得分:0)

请参阅Ruby Recursive Tree

假设我们有

#$ mkdir -p foo/bar
#$ mkdir -p baz/boo/bee
#$ mkdir -p baz/goo

我们可以

{
  "baz"=>{
    "boo"=>{
      "bee"=>{}},
    "goo"=>{}},
  "foo"=>{
    "bar"=>{}}}

我们可以如下遍历树。所以,这是一种基于磁盘上的目录树制作哈希的方法:

Dir.glob('**/*'). # get all files below current dir
  select{|f|
    File.directory?(f) # only directories we need
  }.map{|path|
    path.split '/' # split to parts
  }.inject({}){|acc, path| # start with empty hash
    path.inject(acc) do |acc2,dir| # for each path part, create a child of current node
      acc2[dir] ||= {} # and pass it as new current node
    end
    acc
  }

感谢MladenJablanović在这个概念的另一个答案中。