Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate over a deeply nested level of hashes in Ruby

So I have a hash, and for each level of the hash, I want to store its key and value. The problem is, a value can be another hash array. Furthermore, that hash can contain key value pairs where the value is again another hash array, etc, etc. Also, I won't know how deeply nested each hash will be. To give an example:

{   :key1 => 'value1',   :key2 => 'value2',   :key3 => {      :key4 => 'value4',      :key5 => 'value5'    },     :key6 => {       :key7 => 'value7',       :key8 => {         :key9 => 'value9'       }     }   } 

..And so on. What I want to do is save each key, value pair and the id of its parent. I figure this will probably be done recursively, I'm just unsure how because I'm unfamiliar with recursive functions. I know how to iterate through the data normally:

  myHash.each {|key, value|     ...Do something with the key and value ...   } 

And so I'm guessing the recursive call will be something like this:

def save_pair (myHash)   myHash.each {|key, value|     if(value.class != Hash) ? Pair.create(key, value) : save_pair(value)   } end 

This is untested, and I'm still unsure how to incorporate saving the parent ids regardless.

like image 282
varatis Avatar asked Jan 05 '12 19:01

varatis


People also ask

How do I iterate a Hash in Ruby?

Iterating over a Hash You can use the each method to iterate over all the elements in a Hash. However unlike Array#each , when you iterate over a Hash using each , it passes two values to the block: the key and the value of each element.

What is a nested Hash?

Nested hashes allow us to further group, or associate, the data we are working with. They help us to deal with situations in which a category or piece of data is associated not just to one discrete value, but to a collection of values.

What is hash map in Ruby?

A Map is a mapping of Distinct Keys to Values; there are Map variations which relax this, but Ruby's Hashmap follows the standard Map ADT. In this case an Array of two different Hashes (each with a "value" and a "details") is being created.

What is Hash function in Ruby?

In Ruby, Hash is a collection of unique keys and their values. Hash is like an Array, except the indexing is done with the help of arbitrary keys of any object type. In Hash, the order of returning keys and their value by various iterators is arbitrary and will generally not be in the insertion order.


2 Answers

If I understand the goal, then you should be able to pass in the parent to your save method. For the top level, it will be nil. The following shows the idea where puts is used as a place holder for the "save".

def save_pair(parent, myHash)   myHash.each {|key, value|     value.is_a?(Hash) ? save_pair(key, value) :             puts("parent=#{parent.nil? ? 'none':parent}, (#{key}, #{value})")   } end 

Here is an example call to it:

hash = Hash.new hash["key1"] = "value1" hash["key2"] = "value2" hash["key3"] = Hash.new hash["key3"]["key4"] = "value4" hash["key3"]["key5"] = "value5" hash["key6"] = Hash.new hash["key6"]["key7"] = "value7" hash["key6"]["key8"] = Hash.new hash["key6"]["key8"]["key9"] = "value9"  save_pair(nil, hash) 
like image 196
Mark Wilkins Avatar answered Sep 18 '22 17:09

Mark Wilkins


I know this is a late reply, but I just implemented a non-recursive solution to your problem and thought it is worth sharing.

class Hash   def deep_traverse(&block)     stack = self.map{ |k,v| [ [k], v ] }     while not stack.empty?       key, value = stack.pop       yield(key, value)       if value.is_a? Hash         value.each{ |k,v| stack.push [ key.dup << k, v ] }       end     end   end end 

Then, coming back to your original problem, you can do:

h = {   :key1 => 'value1',   :key2 => 'value2',   :key3 => {      :key4 => 'value4',      :key5 => 'value5'   },   :key6 => {     :key7 => 'value7',     :key8 => {       :key9 => 'value9'     }   } } h.deep_traverse{ |path,value| p [ path, value ] } # => [[:key6], {:key7=>"value7", :key8=>{:key9=>"value9"}}] #    [[:key6, :key8], {:key9=>"value9"}] #    [[:key6, :key8, :key9], "value9"] #    [[:key6, :key7], "value7"] #    [[:key3], {:key4=>"value4", :key5=>"value5"}] #    [[:key3, :key5], "value5"] #    [[:key3, :key4], "value4"] #    [[:key2], "value2"] #    [[:key1], "value1"] 

There is also a gist version.

like image 25
sebastian Avatar answered Sep 20 '22 17:09

sebastian