Hash - nested traversal - Ruby - (Any one know this) - ruby-on-rails

I have this hash
hasha = {"a" => "b","a_a" => {"x_y" => "sreeraj","a_b" => "hereIam"}}
I need to change this to
hasha = {"a" => "b","a-a" => {"x-y" => "sreeraj","a-b" => "hereIam"}}
i.e. I need to change all keys containing "_"(underscore) to "-"(minus). How can I do this?

This is might not be the smarter one, but it works:
def rep_key(hash={})
newhash={}
hash.each_pair do |key,val|
val = rep_key(val) if val.class == Hash
newhash[key.sub(/_/,'-')] = val
end
newhash
end
where:
hasha = {"a" => "b","a_a" => {"x_y" => "sreeraj","a_b" => "hereIam"}}
newhash = rep_key hasha
puts newhash.inspect
gives:
newhash = {"a" => "b","a-a" => {"x-y" => "sreeraj","a-b" => "hereIam"}}

Try recursion.
def replace_all(x, a, b)
return if x.class != Hash
y = Hash.new
x.each do |k,v|
if(v.class == Hash)
v = replace_all(v, a, b)
end
if k.class == String and k.include?(a)
y[k.gsub(a,b)] = v
else
y[k] = v
end
end
return y
end
hasha = {"a" => "b","a_a" => {"x_y" => "sreeraj","a_b" => "hereIam"}}
puts replace_all(hasha, "_", "-")

Related

TypeError no implicit conversion of Symbol into Integer

Hash
data = {
:recordset => {
:row => {
:property => [
{:name => "Code", :value => "C0001"},
{:name => "Customer", :value => "ROSSI MARIO"}
]
}
},
:#xmlns => "http://localhost/test"
}
Code Used
result = data[:recordset][:row].each_with_object([]) do |hash, out|
out << hash[:property].each_with_object({}) do |h, o|
o[h[:name]] = h[:value]
end
end
I cannot get the following output:
[{"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"}
Error message:
TypeError no implicit conversion of Symbol into Integer
It works correctly in case of multi records
data = {
:recordset => {
:row => [{
:property => [
{:name => "Code", :value => "C0001"},
{:name => "Customer", :value => "ROSSI MARIO"},
{:name => "Phone1", :value => "1234567890"}
]
}, {
:property => [
{:name => "Code", :value => "C0002"},
{:name => "Customer", :value => "VERDE VINCENT"},
{:name => "Phone1", :value => "9876543210"},
{:name => "Phone2", :value => "2468101214"}
]
}]
},
:#xmlns => "http://localhost/test"
}
Code used
data.keys
#=> [:recordset, :#xmlns]
data[:recordset][:row].count
#=> 2 # There are 2 set of attribute-value pairs
result = data[:recordset][:row].each_with_object([]) do |hash, out|
out << hash[:property].each_with_object({}) do |h, o|
o[h[:name]] = h[:value]
end
end
#=> [
# {"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"},
# {"Code"=>"C0002", "Customer"=>"VERDE VINCENT", "Phone1"=>"9876543210", "Phone2"=>"2468101214"}
# ]
In the first case data[:recordset][:row] is not an Array, it's a Hash, so when you iterate it, the hash variable becomes the array:
[:property, [{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}]]
In the second case, it's an Array, not a Hash, so when you iterate it, it becomes the hash:
{:property=>[{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}, {:name=>"Phone1", :value=>"1234567890"}]}
You're always assuming it's the second format. You could force it into an array, and then flatten by 1 level to treat both instances the same:
result = [data[:recordset][:row]].flatten(1).each_with_object([]) do |hash, out|
out << hash[:property].each_with_object({}) do |h, o|
o[h[:name]] = h[:value]
end
end
# => [{"Code"=>"C0001", "Customer"=>"ROSSI MARIO"}] # result from example 1
# => [{"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"},
# {"Code"=>"C0002", "Customer"=>"VERDE VINCENT",
# "Phone1"=>"9876543210", "Phone2"=>"2468101214"}] # result from example 2
It's tempting to try and use Kernal#Array() instead of [].flatten(1), but you have to remember that Hash implements to_a to return a nested array of keys and values, so Kernal#Array() doesn't work like you'd want it to:
Array(data[:recordset][:row]) # using the first example data
# => [[:property, [{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}]]]
You can create an array if it's not an array to normalize the input before processing it.
info = data[:recordset][:row]
info = [info] unless info.is_an? Array
result = info.each_with_object([]) do ....

Flattening nested hash to a single hash with Ruby/Rails

I want to "flatten" (not in the classical sense of .flatten) down a hash with varying levels of depth, like this:
{
:foo => "bar",
:hello => {
:world => "Hello World",
:bro => "What's up dude?",
},
:a => {
:b => {
:c => "d"
}
}
}
down into a hash with one single level, and all the nested keys merged into one string, so it would become this:
{
:foo => "bar",
:"hello.world" => "Hello World",
:"hello.bro" => "What's up dude?",
:"a.b.c" => "d"
}
but I can't think of a good way to do it. It's a bit like the deep_ helper functions that Rails adds to Hashes, but not quite the same. I know recursion would be the way to go here, but I've never written a recursive function in Ruby.
You could do this:
def flatten_hash(hash)
hash.each_with_object({}) do |(k, v), h|
if v.is_a? Hash
flatten_hash(v).map do |h_k, h_v|
h["#{k}.#{h_k}".to_sym] = h_v
end
else
h[k] = v
end
end
end
flatten_hash(:foo => "bar",
:hello => {
:world => "Hello World",
:bro => "What's up dude?",
},
:a => {
:b => {
:c => "d"
}
})
# => {:foo=>"bar",
# => :"hello.world"=>"Hello World",
# => :"hello.bro"=>"What's up dude?",
# => :"a.b.c"=>"d"}
Because I love Enumerable#reduce and hate lines apparently:
def flatten_hash(param, prefix=nil)
param.each_pair.reduce({}) do |a, (k, v)|
v.is_a?(Hash) ? a.merge(flatten_hash(v, "#{prefix}#{k}.")) : a.merge("#{prefix}#{k}".to_sym => v)
end
end
irb(main):118:0> flatten_hash(hash)
=> {:foo=>"bar", :"hello.world"=>"Hello World", :"hello.bro"=>"What's up dude?", :"a.b.c"=>"d"}
The top voted answer here will not flatten the object all the way, it does not flatten arrays. I've corrected this below and have offered a comparison:
x = { x: 0, y: { x: 1 }, z: [ { y: 0, x: 2 }, 4 ] }
def top_voter_function ( hash )
hash.each_with_object( {} ) do |( k, v ), h|
if v.is_a? Hash
top_voter_function( v ).map do |h_k, h_v|
h[ "#{k}.#{h_k}".to_sym ] = h_v
end
else
h[k] = v
end
end
end
def better_function ( a_el, a_k = nil )
result = {}
a_el = a_el.as_json
a_el.map do |k, v|
k = "#{a_k}.#{k}" if a_k.present?
result.merge!( [Hash, Array].include?( v.class ) ? better_function( v, k ) : ( { k => v } ) )
end if a_el.is_a?( Hash )
a_el.uniq.each_with_index do |o, i|
i = "#{a_k}.#{i}" if a_k.present?
result.merge!( [Hash, Array].include?( o.class ) ? better_function( o, i ) : ( { i => o } ) )
end if a_el.is_a?( Array )
result
end
top_voter_function( x ) #=> {:x=>0, :"y.x"=>1, :z=>[{:y=>0, :x=>2}, 4]}
better_function( x ) #=> {"x"=>0, "y.x"=>1, "z.0.y"=>0, "z.0.x"=>2, "z.1"=>4}
I appreciate that this question is a little old, I went looking online for a comparison of my code above and this is what I found. It works really well when used with events for an analytics service like Mixpanel.
Or if you want a monkey-patched version or Uri's answer to go your_hash.flatten_to_root:
class Hash
def flatten_to_root
self.each_with_object({}) do |(k, v), h|
if v.is_a? Hash
v.flatten_to_root.map do |h_k, h_v|
h["#{k}.#{h_k}".to_sym] = h_v
end
else
h[k] = v
end
end
end
end
In my case I was working with the Parameters class so none of the above solutions worked for me. What I did to resolve the problem was to create the following function:
def flatten_params(param, extracted = {})
param.each do |key, value|
if value.is_a? ActionController::Parameters
flatten_params(value, extracted)
else
extracted.merge!("#{key}": value)
end
end
extracted
end
Then you can use it like flatten_parameters = flatten_params(params). Hope this helps.
Just in case, that you want to keep their parent
def flatten_hash(param)
param.each_pair.reduce({}) do |a, (k, v)|
v.is_a?(Hash) ? a.merge({ k.to_sym => '' }, flatten_hash(v)) : a.merge(k.to_sym => v)
end
end
hash = {:foo=>"bar", :hello=>{:world=>"Hello World", :bro=>"What's up dude?"}, :a=>{:b=>{:c=>"d"}}}
flatten_hash(hash)
# {:foo=>"bar", :hello=>"", :world=>"Hello World", :bro=>"What's up dude?", :a=>"", :b=>"", :c=>"d"}

How can i save the conditions in variable and use that variable in rails active record?

I am trying to save the conditions in a variable and call that in the active record query as shown below
if !key1.nil?
#condition = ":key2 => #value2, :key3 => #value3"
else
#condition = ":key4 => #value4, :key5 => #value5"
end
#result = Model.where(#condition).all
How can i do this? please help me.
UPDATE:
#condition = { "key1 = ? and key2 >= ? and key3 <= ? and id IN (?)", #value1, #value2, #value3, #id }
Use hash instead of string:
#condition = { :key2 => #value2, :key3 => #value3 }
Also, you could probably simplify the syntax of if !key1.nil? by using unless:
#condition = {}
unless key1
#condition = { :key2 => #value2, :key3 => #value3 }
else
#condition = { :key4 => #value4, :key5 => #value5 }
end

How to return hash values as a string using to_s

This is my code:
def return_rider_values(pol_option, pro_endorsement, prop_coverage, *par)
rider_values
par.each do |p|
rider_values << RiderValue.find_all_by_rider_id(p)
end
rider_hash = { }
rider_values.each do |rv|
if rv.attributes["name"].downcase == "yes"
rider_hash.merge!({par[0].to_s => rv.attributes['id'].to_s})
elsif rv.attributes["position"] == pol_option.to_i && rv.attributes["rider_id"] == par[1]
rider_hash.merge!({par[1].to_s => rv.attributes["id"].to_s})
elsif rv.attributes["position"] == prop_coverage.to_i && rv.attributes["rider_id"] == par[2]
rider_hash.merge!({par[2].to_s => rv.attributes["id"].to_s})
elsif rv.attributes["position"] == pro_endorsement.to_i && rv.attributes["rider_id"] == par[3]
rider_hash.merge!({par[3].to_s => rv.attributes["id"].to_s})
end
end
rider_hash
end
The output looks like this:
rider_hash #=> '22' -> 58
'23' -> 61
'25' -> 66
'26' -> 68
I was expecting, and need apparently since it's not working later down the line:
rider_hash #=> '22' -> '58'
'23' -> '61'
'25' -> '66'
'26' -> '68'
I don't know why the lookup function later in the program wants the ids to be strings instead of ints. I just know that it does, and I can't change it since lots of other methods use it.
I have to_s on both the hash key and value. I realize that in Ruby 1.9 to_s is an alias for inspect but even in the Hash documentation it says that, inspect or to_s is supposed to "Return the contents of this hash as a string."
So why is only the key being returned as a string?
You have an array of hashes so try this:
def return_rider_values
par = [1,2,3,6]
rider_hash = {}
rider_values = [element1: {:attributes => {:id => 1, :name => 'yes', :position => 1, :rider_id => 1}},
element2: {:attributes => {:id => 2, :name => 'no', :position => 2, :rider_id => 2}},
element3: {:attributes => {:id => 3, :name => 'something', :position => 1, :rider_id => 3}},
element4: {:attributes => {:id => 4, :name => 'something_else', :position => 2, :rider_id => 6}}]
rider_values.each_with_index do |hash, idx|
rider_values[idx].each_pair do |k, v|
if v[:attributes][:name].downcase == "yes"
rider_hash.merge!({par[0].to_s => v[:attributes][:id].to_s})
elsif v[:attributes][:position] == 2 && v[:attributes][:rider_id] == par[1]
rider_hash.merge!({par[1].to_s => v[:attributes][:id].to_s})
elsif v[:attributes][:position] == 3 && v[:attributes][:rider_id] == par[2]
rider_hash.merge!({par[2].to_s => v[:attributes][:id].to_s})
elsif v[:attributes][:position] == 4 && v[:attributes][:rider_id] == par[3]
rider_hash.merge!({par[3].to_s => v[:attributes][:id].to_s})
end
end
end
rider_hash
end
test = return_rider_values
puts test
output: #=> {"1"=>"1", "2"=>"2"}
I ended up getting what I wanted by adding this:
rider_hash.each{ |_,v| v.replace "'#{v}'"}
But this seems like a dirty solution somehow.

how to create hash within hash

How am I able to create a hash within a hash, with the nested hash having a key to indentify it. Also the elements that I create in the nested hash, how can I have keys for them as well
for example
test = Hash.new()
#create second hash with a name?? test = Hash.new("test1")??
test("test1")[1] = 1???
test("test1")[2] = 2???
#create second hash with a name/key test = Hash.new("test2")???
test("test2")[1] = 1??
test("test2")[2] = 2??
thank you
Joel's is what I would do, but could also do this:
test = Hash.new()
test['test1'] = Hash.new()
test['test1']['key'] = 'val'
my_hash = { :nested_hash => { :first_key => 'Hello' } }
puts my_hash[:nested_hash][:first_key]
$ Hello
or
my_hash = {}
my_hash.merge!(:nested_hash => {:first_key => 'Hello' })
puts my_hash[:nested_hash][:first_key]
$ Hello
h1 = {'h2.1' => {'foo' => 'this', 'cool' => 'guy'}, 'h2.2' => {'bar' => '2000'} }
h1['h2.1'] # => {'foo' => 'this', 'cool' => 'guy'}
h1['h2.2'] # => {'bar' => '2000'}
h1['h2.1']['foo'] # => 'this'
h1['h2.1']['cool'] # => 'guy'
h1['h2.2']['bar'] # => '2000'

Resources