Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

print out Hash key and value pairs recursively

Tags:

ruby

I am trying to define a function that it can print out any hash values in a tree format. The function will do something like this:

From

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

To

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

And this is my code so far:

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 

and it will give me a bad format like this:

parent1:
child1:
grandchild1:
1
   grandchild2:
2
   child2:
grandchild3:
3
   grandchild4:
4
like image 340
lilixiaocc Avatar asked Aug 22 '16 18:08

lilixiaocc


3 Answers

You are almost there. One way to solve it is to make level a part of the recursive function parameters. x is the hash in the question.

Simple recursive version:

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

In this case you could also convert it to YAML (as mentioned in a comment above)

require 'YAML'
puts x.to_yaml
#---
#parent1:
#  child1:
#    grandchild1: 1
#    grandchild2: 2
#  child2:
#    grandchild3: 3
#    grandchild4: 4
like image 69
hirolau Avatar answered Nov 10 '22 07:11

hirolau


I would use recursion, but there is another way that might be of interest to some. Below I've used a "pretty printer", awesome-print, to do part of the formatting (the indentation in particular), saving the result to a string, and then applied a couple of gsub's to the string to massage the results into the desired format.

Suppose your hash were as follows:

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

We could then do the following.

require 'awesome_print'

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

prints

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

The steps:

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

puts str prints

{
     "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 prints

 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 prints the result above.

like image 42
Cary Swoveland Avatar answered Nov 10 '22 09:11

Cary Swoveland


Not exactly what you require but I will submit this answer as I think you may find it useful:

require 'yaml'

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

puts hash.to_yaml

prints:

---
parent1:
  child1:
    grandchild1: 1
    grandchild2: 2
  child2:
    grandchild3: 3
    grandchild4: 4
like image 1
Sagar Pandya Avatar answered Nov 10 '22 07:11

Sagar Pandya