rubyhash-of-hashes

symbolizing and replacing a value in hash values in hash of array of hash


I have a data structure which is something similar to this

  lib_data = {'library'=>[{"type"=>"Books", 
  "checkout"=>false, "data"=>[{"type"=>"Books", "operator"=>"AND", "details"=>{"property"=>"Name", "author"=>"%s", 
  "name"=>"%s"}}, {"type"=>"Books", "criteria"=>{"property"=>"Revision", "operator"=>"%s", 
  "value"=>"%s"}}]}]}

I am trying to symobolize all the keys first and then insert values in keys represented by inplace of %s

I was able to monkey patch a small bit of code into Hash class that would gets me to symbolize all the hash values using something like this

def symbolize_all_keys
    keys.each do |k|
      key = k.to_sym
      value = delete(k)
      store(key, value)
    end
    self
  end

and then just use lib_data.symbolize_all_keys but this symbolizes one key

Is there a more idomatic way to symbolize all keys ?

also wanted a way insert values into the keys that have %s values?


Solution

  • You could use Hash#transform_keys! in a recursive way, something like:

    def symbolize_all_keys(h)
      if h.is_a? Hash
        h.transform_keys!(&:to_sym)
        h.values.each do |val|
          val.each { |v| symbolize_all_keys(v) } if val.is_a? Array
          symbolize_all_keys(val)
        end
      end
      h
    end
    
    symbolize_all_keys(lib_data)
    
    lib_data
    #=> {:library=>[{:type=>"Books", :checkout=>false, :data=>[{:type=>"Books", :operator=>"AND", :details=>{:property=>"Name", :author=>"%s", :name=>"%s"}}, {:type=>"Books", :criteria=>{:property=>"Revision", :operator=>"%s", :value=>"%s"}}]}]}
    

    For the second part, not clear which value is replacing "%s" depending on the key.

    Just an idea, similar to the recursive one above:

    def replace_val(h, new_val = 'new_val')
      if h.is_a? Hash
        h.each do |key, val|
          h[key] = new_val if val == '%s'
          val.each { |v| replace_val(v) } if val.is_a? Array
          replace_val(val)
        end
      end
      h
    end
    
    replace_val(lib_data)
    
    lib_data
    #=> {:library=>[{:type=>"Books", :checkout=>false, :data=>[{:type=>"Books", :operator=>"AND", :details=>{:property=>"Name", :author=>"new_val", :name=>"new_val"}}, {:type=>"Books", :criteria=>{:property=>"Revision", :operator=>"new_val", :value=>"new_val"}}]}]}
    

    Here you have access to the key of the value, but not to higher levels.