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

160 views Asked by At

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?

2

There are 2 answers

1
iGian On BEST ANSWER

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.

0
Salil On

If you are using rails, you can use HashWithIndifferentAccess

You can also access this class outside of Rails, require the core extension with:

require "active_support/core_ext/hash/indifferent_access"

2.6.5 :015 > lib_data = {'library'=>[{"type"=>"Books",
2.6.5 :016 >           "checkout"=>false, "data"=>[{"type"=>"Books", "operator"=>"AND", "details"=>{"property"=>"Name", "author"=>"%s",
2.6.5 :017 >                 "name"=>"%s"}}, {"type"=>"Books", "criteria"=>{"property"=>"Revision", "operator"=>"%s",
2.6.5 :018 >                 "value"=>"%s"}}]}]}
 => {"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"}}]}]}
2.6.5 :019 > lib_data[:library]
 => nil
2.6.5 :020 > lib_data = lib_data.with_indifferent_access
 => {"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"}}]}]}
2.6.5 :021 > 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"}}]}]