How would I parse a flat Ruby hash in to a nested hash?
First you can split up the hash into multiple hashes according to their prefix number (feel free to run this to see the return val)
groups = input.
group_by { |k,v| k.match(/builder_rule_(\d+)/)[1] }.
transform_values(&:to_h)
at this point, creating the inner objects is easier if you just use some hard-coding to build the key-valls:
result = groups.each_with_object({}) do |(prefix, hash), memo|
memo[prefix] = {
"filter" => hash["builder_rule_#{prefix}_filter"],
"operator" => hash["builder_rule_#{prefix}_operator"],
"values" => hash.select do |key, val|
key =~ /builder_rule_#{prefix}_value/
end.sort_by { |key, val| key }.map { |(key, val)| val }
}
end
It might possibly be confusing what .sort_by { |key, val| key }.map { |(key, val)| val }
means. I can spell it out:
hash.select { |key, val| key =~ /builder_rule_#{prefix}_value/ }
gets the key-vals which are going to be used for the "values" array. It returns a hash..sort_by { |key, val| key }
turns the hash into an array of[key, val]
tuples , sorted by the key. This is so that the values appear in the correct order..map { |(key, val)| val }
turns the nested array into a single-level array, discarding the keys
You could also use sort_by(&:first).map(&:second)
, though you need active support to use Array#second
Converting a hash into a nested hash
Here's an iterative solution, a recursive one is left as an exercise to the reader:
def convert(h={})
ret = {}
h.each do |k,v|
node = ret
k[0..-2].each {|x| node[x]||={}; node=node[x]}
node[k[-1]] = v
end
ret
end
convert(your_hash) # => {:f=>4, :a=>{:b=>{:c=>1, :d=>2}, :e=>3}}
How to trasform all values in a nested hash?
Interesting to learn of the deep_merge
approach taken in the answer by "The F". Here is another approach which requires adding a few helper methods.
First, the helper methods:
From the top answer here (converting-a-nested-hash-into-a-flat-hash):
def flat_hash(h,f=[],g={})
return g.update({ f=>h }) unless h.is_a? Hash
h.each { |k,r| flat_hash(r,f+[k],g) }
g
end
From a Github repo called ruby-bury (this functionality was proposed to Ruby core, but rejected)
class Hash
def bury *args
if args.count < 2
raise ArgumentError.new("2 or more arguments required")
elsif args.count == 2
self[args[0]] = args[1]
else
arg = args.shift
self[arg] = {} unless self[arg]
self[arg].bury(*args) unless args.empty?
end
self
end
end
And then a method tying it together:
def change_all_values(hash, &blk)
# the next line makes the method "pure functional"
# but can be removed otherwise.
hash = Marshal.load(Marshal.dump(hash))
flat_hash(hash).each { |k,v| hash.bury(*(k + [blk.call(v)])) }
hash
end
A usage example:
irb(main):063:0> a = {a: 1, b: { c: 1 } }
=> {:a=>1, :b=>{:c=>1}}
irb(main):064:0> b = change_all_values(a) { |val| val + 1 }
=> {:a=>2, :b=>{:c=>2}}
irb(main):066:0> a
=> {:a=>1, :b=>{:c=>1}}
How to convert a string into a nested hash
First of all, you have to split
the string. You can then use inject
to build the structure. Because the hash is built from the inside out, we have to reverse
the initial array:
'foo.bar.baz'.split('.') #=> ["foo", "bar", "baz"]
.reverse #=> ["baz", "bar", "foo"]
.inject('qux') { |memo, key| { key.to_sym => memo } }
#=> {:foo=>{:bar=>{:baz=>"qux"}}}
It might not be obvious how this works. On the first invocation, memo
is 'qux'
and key
is 'baz'
. The block turns this into {:baz=>'qux'}
which then becomes the new memo
.
Step by step:
memo key result
--------------------------------------------------------------
'qux' 'baz' {:baz=>'qux'}
{:baz=>'qux'} 'bar' {:bar=>{:baz=>'qux'}}
{:bar=>{:baz=>'qux'}} 'foo' {:foo=>{:bar=>{:baz=>'qux'}}}
Insert new key value into nested hash in Ruby
You need to construct a new array from your existing:
photos = [{"id": "111","photo": "http://photo.com/111.jpeg"}, {"id": "222", "photo": "http://photo.com/222.jpeg"}]
new_photos = photos.map do |photo|
if photo[:id] == '111'
photo.merge(type: 'Profile')
else
photo
end
end
How to convert hash with keys representing nesting into a nested hash
After some research I found a way to parse nested query keys using http://apidock.com/rails/Rack/Utils/parse_nested_query:
Rack::Utils.parse_nested_query('item[0][size]')
=> {
"item" => {
"0" => {
"size" => nil
}
}
}
So it's now possible to do:
items_string = item_hash.to_a.map { |row| row.join('=') }.join('&')
result = Rack::Utils.parse_nested_query(items_string)
=> {
"item" => {
"0" => {
"size" => "12",
"count" => "1"
}
}
}
Convert array into nested hash for each element
First, a function to loop through the array and split those strings into an array which is much easier to work with.
def nest_array(array)
return array.each_with_object({}) do |string, hash|
values = string.split(/,/)
do_nesting(values, hash)
end
end
Then a recursive function to deal with each individual entry. ['a']
becomes { 'a' => nil }
, ['a', 'b']
becomes { 'a' => 'b' }
and ['a', 'b', 'c']
recurses to make a new hash from ['b', 'c']
.
def do_nesting(values, hash)
if values.size <= 2
hash[values[0]] = values[1]
else
hash[values.shift] = do_nesting(values, {})
end
return hash
end
Related Topics
How to Make a Post Request with Open-Uri
Get Sidekiq to Execute a Job Immediately
Do I Have to Manually Uninstall All Dependent Gems
How to Overwrite a Printed Line in the Shell with Ruby
Notices for Sequence After Running Migration in Rails on Postgresql Application
How to Use Gems Not in a Gemfile When Working with Bundler
Why Doesn't "Rails S" Work from the App Directory
Rails Carrierwave Base64 Image Upload
Rails - Local Variables Versus Instance Variables
How to Generate Links with Trailing Slash in Rails 3
Create Custom HTML Helpers in Ruby on Rails
What Does "Shadowing" Mean in Ruby
How to Solve Insecure World Writable Dir /Usr in Path,Mode 040777 Warning on Ruby
Testing If a Hash Has Any of a Number of Keys
Ruby: Creating a Sandboxed Eval