I am using Ruby on Rails 4.1 and I would like to permit the following incoming parameters by using the StrongParameters gem:
# Parameters:
{
"my_key" => {
"one" => {
"0" => { "a" => "a_value", "b" => "b_value"},
"1" => { "a" => "a_value", "b" => "b_value"},
"2" => { "a" => "a_value", "b" => "b_value"}
},
"two" => {
"0" => { "c" => "c_value", "d" => "d_value"},
"1" => { "c" => "c_value", "d" => "d_value"},
"2" => { "c" => "c_value", "d" => "d_value"}
}
}
}
In controller I tried
params
.require(:my_key)
.permit(
[
:one => [
"0" => [:a, :b],
"1" => [:a, :b],
"2" => [:a, :b]
],
:two => [
"0" => [:c, :d],
"1" => [:c, :d],
"2" => [:c, :d]
]
]
)
and
params
.require(:my_key)
.permit(
{
:one => {
"0" => [:a, :b],
"1" => [:a, :b],
"2" => [:a, :b]
},
:two => {
"0" => [:c, :d],
"1" => [:c, :d],
"2" => [:c, :d]
}
}
)
But I get the error
ActionController::UnpermittedParameters (found unpermitted parameters: a, b)
How above parameters should be permitted?
Here is what you need to do:
Remove Strong Parameters gem from your Gemfile.
Use this in the controller.
params.require(:my_key).permit({:one=>[:a, :b],:two=>[:c, :d]})
I think this has something to do with how nested attributes work. The ids "0", "1", "2" etc are implicit.
You can test in the console like this:
$ bin/rails c
Loading development environment (Rails 4.1.2)
2.1.0 :001 > params = ActionController::Parameters.new "my_key"=>{"one"=>{"0"=>{"a"=>"a_value","b"=>"b_value"},"1"=>{"a"=>"a_value","b"=>"b_value"},"2"=>{"a"=>"a_value","b"=>"b_value"}},"two"=>{"0"=>{"c"=>"c_value","d"=>"d_value"},"1"=>{"c"=>"c_value","d"=>"d_value"},"2"=>{"c"=>"c_value","d"=>"d_value"}}}
=> {"my_key"=>{"one"=>{"0"=>{"a"=>"a_value", "b"=>"b_value"}, "1"=>{"a"=>"a_value", "b"=>"b_value"}, "2"=>{"a"=>"a_value", "b"=>"b_value"}}, "two"=>{"0"=>{"c"=>"c_value", "d"=>"d_value"}, "1"=>{"c"=>"c_value", "d"=>"d_value"}, "2"=>{"c"=>"c_value", "d"=>"d_value"}}}}
2.1.0 :002 > p = params.require(:my_key).permit({:one=>[:a, :b],:two=>[:c, :d]})
=> {"one"=>{"0"=>{"a"=>"a_value", "b"=>"b_value"}, "1"=>{"a"=>"a_value", "b"=>"b_value"}, "2"=>{"a"=>"a_value", "b"=>"b_value"}}, "two"=>{"0"=>{"c"=>"c_value", "d"=>"d_value"}, "1"=>{"c"=>"c_value", "d"=>"d_value"}, "2"=>{"c"=>"c_value", "d"=>"d_value"}}}
2.1.0 :003 >
Related
I have long list of values in the inner_value field from which I want only some values
I have array in this format:
hash_array = [
{
"array_value" => 1,
"inner_value" => [
{"iwantthis" => "forFirst"},
{"iwantthis2" => "forFirst2"},
{"Idontwantthis" => "some value"},
{"iwantthis3" => "forFirst3"},
{"Idontwantthis2" => "some value"},
{"Idontwantthis3" => "some value"},
{"Idontwantthis4" => "some value"},
{"Idontwantthis5" => "some value"},
{"Idontwantthis6" => "some value"},
]
},
{
"array_value" => 2,
"inner_value" => [
{"iwantthis" => "forSecond"},
{"Idontwantthis" => "some value"},
{"iwantthis3" => "forSecond3"},
{"iwantthis2" => "forSecond2"},
{"Idontwantthis2" => "some value"},
{"Idontwantthis3" => "some value"},
{"Idontwantthis4" => "some value"},
{"Idontwantthis5" => "some value"},
{"Idontwantthis6" => "some value"},
]
},
]
Desired Output:
[
{
"array_value" => 1,
"inner_value" => [
{"iwantthis" => "forFirst"},
{"iwantthis2" => "forFirst2"},
{"iwantthis3" => "forFirst3"}
]
},
{
"array_value" => 2,
"inner_value" => [
{"iwantthis" => "forSecond"},
{"iwantthis2" => "forSecond2"},
{"iwantthis3" => "forSecond3"}
]
},
]
I have tried using running loop in this but its too much costly.
So I tried something like this:
hash_array.select { |x| x["inner_value"].select {|y| !y["iwantthis"].nil? } }
but this ain't working either..
Note:Order/Sort does not matter
Your aim is not to select, you have to modify the input:
hash_array.map { |hash| hash['inner_value'] = hash['inner_value'].first }
#=> [
# {
# "array_value"=>1,
# "inner_value"=> {
# "iwantthis"=>"forFirst"
# }
# },
# {
# "array_value"=>2,
# "inner_value"=> {
# "iwantthis"=>"forSecond"
# }
# }
# ]
Here you'd basically change the value of whole hash['inner_value'] to what you want.
To do this with known key:
hash_array.map do |hash|
hash['inner_value'] = hash['inner_value'].find { |hash| hash['iwantthis'] }
end # `iwantthis` is the key, that can change
For multiple keys:
keys = %w(iwantthis Idontwantthis)
hash_array.map do |hash|
hash['inner_value'] = keys.flat_map do |key|
hash['inner_value'].select {|hash| hash if hash[key] }
end
end
#=> [{"array_value"=>1, "inner_value"=>[{"iwantthis"=>"forFirst"}, {"Idontwantthis"=>"some value"}]}, {"array_value"=>2, "inner_value"=>[{"iwantthis"=>"forSecond"}, {"Idontwantthis"=>"some value"}]}]
you can use map
hash_array.map{|k| {"array_value" => k['array_value'], 'inner_value' => k['inner_value'][0]} }
#=> [{"array_value"=>1, "inner_value"=>{"iwantthis"=>"forFirst"}}, {"array_value"=>2, "inner_value"=>{"iwantthis"=>"forSecond"}}]
Given a Ruby hash of parameters that are infinitely nested, I want to write a function that returns true if a given key is in those parameters.
This is the function I have so far, but it's not quite right, and I'm at a loss as to why:
def has_key(hash, key)
hash.each do |k, v|
if k == key
return true
elsif v.class.to_s == "Array"
v.each do |inner_hash|
return has_key(inner_hash,key)
end
else
return false
end
end
end
The method should return the following results:
# all check for presence of "refund" key
has_key({
"refund" => "2"
}, "refund")
=> true
has_key({
"whatever" => "3"
}, "refund")
=> false
has_key({
"whatever" => "3",
"child_attributes" => [{
"refund" => "1"
}]
}, "refund")
=> true
has_key({
"whatever" => "3",
"child_attributes" => [{
"nope" => "4"
}]
}, "refund")
=> false
has_key({
"whatever" => "3",
"child_attributes" => [{
"a" => "1",
"refund" => "2"
}]
}, "refund")
=> true
has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"aa" => "1", "refund" => "2"}
]
}, "refund")
=> true
has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3"}
]}
]
}, "refund")
=> false
has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3"}, {"refund" => "5"}
]}
]
}, "refund")
=> true
has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3", "refund" => "5"}
]}
]
}, "refund")
=> true
The problem with your code seems to be in here:
elsif v.class.to_s == "Array"
v.each do |inner_hash|
return has_key(inner_hash,key)
end
else
This would always return has_key(inner_array[0]) without checking subsequent values. The fix is to return only if it's true, else continue checking, like this:
elsif v.class.to_s == "Array"
v.each do |inner_hash|
if(has_key(inner_hash,key))
return true
end
end
else
return false
The following will work.
def has_key(hash, key)
hash.each do |k, v|
return true if k == key
if v.is_a? Array
v.each do |h|
rv = has_key(h, key)
return rv if rv
end
end
end
false
end
This passes all your tests. One more:
h = { "a" => 1,
"b" => [{ "c" => 2, "d" => 3 },
{"e"=> [{ "f" => "4" },
{ "g" => [{ "h" => 5 },
{ "i" => 6, "refund" => 7 }
]
}
]
}
]
}
has_key h, "refund"
#=> true
h["b"][1]["e"][1]["g"] = [{ "h"=>5 }]
h
#=> {"a"=>1, "b"=>[{"c"=>2, "d"=>3}, {"e"=>[{"f"=>"4"}, {"g"=>[{"h"=>5}]}]}]}
has_key h, "refund"
#=> false
Inspired by #Wand's answer, for
h = {"a"=>"3", "b"=>[{"c"=>"1", "d"=>"2"}, {"e"=>[{"test"=>"3", "refund"=>"5"}]}]}
you don't have to load JSON:
str = h.to_s
#=> "{\"a\"=>\"3\", \"b\"=>[{\"c\"=>\"1\", \"d\"=>\"2\"}, {\"e\"=>[{\"test\"=>\"3\", \"refund\"=>\"5\"}]}]}"
str =~ /\"refund\"=>/
#=> 60 (truthy)
I confess to being a little uncomfortable with any approach that converts the hash to a string, and then parsing the string, for fear that string formats may change in future.
I'd do something like:
class Hash
def key_exists?(key)
self.keys.include?(key) ||
self.values.any?{ |v|
Hash === v &&
v.key_exists?(key)
}
end
end
{'a' => 1}.key_exists?('a') # => true
{'b' => 1}.key_exists?('a') # => false
{'b' => {}}.key_exists?('a') # => false
{'b' => {'a' => {}}}.key_exists?('a') # => true
{'b' => {'a' => 1}}.key_exists?('a') # => true
{'b' => {'b' => {}}}.key_exists?('a') # => false
{'b' => {'b' => {'a' => 1}}}.key_exists?('a') # => true
Insert all the usual warnings about extending core classes and recommendations to use the alternative ways of doing it here.
Note: Similarly, "iterate over every key in nested hash" could be used to easily determine a true/false value and it demonstrates the safe way to extend a core class.
You could convert the hash to JSON and then check whether the JSON has "refund": present in it, as any key will be serialised to JSON in the form "key":.
require "json"
hash.to_json.include?('"refund":')
Give this a shot, I tried it, seems to work fine for me
def has_key(hash, key)
if hash.keys.include?(key)
true
else
# get the values of the current hash, flatten out to not include arrays
new_hash = hash.values.flatten
# then filter out any element that's not a hash
new_hash = new_hash.select {|b| b.is_a?(Hash)}
# merge all the hashes into single hash
new_hash = new_hash.inject {|first, second| first.merge(second)}
if new_hash
has_key(new_hash, key)
else
false
end
end
end
I would like to transform this
def some_process(k,v)
return "#{v}_#{k}"
end
a_hash = {
"i_love_hashes" => {
"thing" => 20,
"other_thing" => "0",
"yet_another_thing" => "i disagree",
"_peculiar_thing" => [
{"count" => 30,
"name" => "freddie"},
{"count" => 15,
"name" => "johhno"},
{"count" => 12,
"name" => "mohammed"},
]
},
"as_do_i" => {
"thing" => 10,
"other_thing" => "2",
"yet_another_thing" => "i strongly agree",
"_peculiar_thing" => [
{"count" => 10,
"name" => "frodo"},
{"count" => 4,
"name" => "bilbo"},
{"count" => 2,
"name" => "elizabeth"},
]
}
}
into this
{
"i_love_hashes"=>{
"thing"=>20,
"other_thing"=>"0",
"yet_another_thing"=>"i disagree",
"_peculiar_thing"=> [
{"count"=>30, "name"=>"freddie", :sinister_name=>"freddie_i_love_hashes"},
{"count"=>15, "name"=>"johhno", :sinister_name=>"johhno_i_love_hashes"},
{"count"=>12, "name"=>"mohammed", :sinister_name=>"mohammed_i_love_hashes"}
]},
"as_do_i"=>{
"thing"=>10,
"other_thing"=>"2",
"yet_another_thing"=>"i strongly agree",
"_peculiar_thing"=>[
{"count"=>10, "name"=>"frodo", :sinister_name=>"frodo_as_do_i"},
{"count"=>4, "name"=>"bilbo", :sinister_name=>"bilbo_as_do_i"},
{"count"=>2, "name"=>"elizabeth", :sinister_name=>"elizabeth_as_do_i"}
]
}
}
this is the code I am currently using to achieve this
a_hash.each_with_object({}) do |(k,v),o|
o.merge!({k =>
v.each_with_object({}) do |(a,b),g|
g.merge!({ a =>
(b.is_a?(Array) ? b.collect {|x| x.merge({sinister_name: (some_process k, x["name"])})} : b)
})
end
})
end
Ignoring the specific details of what is being returned by "some_process" (what is important is that it depends on the outer most key and the inner name values, in this example), are there any alternatives that would be considered more elegant?
Why not do a recursive function?
def add_siniter(hash)
hash[:siniter_name] = "#{hash['name']}_i_love_hashes"
hash
end
def format_hash(item)
case item
when Hash then item.keys.each{|key| format_hash(item[key])}
when Array then item.map!{|h| add_siniter(h)}
end
end
format_hash(a_hash)
I am a newbie in Ruby and I am trying to learn Ruby these day. I was going through Hash today and got stuck in problem related to Hash
I have the following Hash
{"key1" => ["param_1","param_2"], "key2" => ["param_3","param_4"], "key3" => "param_5", "key4" => "param_6","key5" => ["param_7","param_8"]}
and I want to convert the above Hash into the following.
{"my_hash" => [ {"name" => "key1","value" => ["param_1","param_2"]},
{"name" => "key2","value" => ["param_3","param_4"]},
{"name" => "key3","value" => ["param_5"]},
{"name" => "key4","value" => ["param_6"]},
{"name" => "key5","value" => ["param_7","param_8"]}
]
}
Can someone show me how can i do it in Ruby in a efficient way.
hsh = {"key1" => ["param_1","param_2"],
"key2" => ["param_3","param_4"], "key3" => "param_5",
"key4" => "param_6","key5" => ["param_7","param_8"]}
hsh.map{|k,v| {name: k,value: Array(v) }}
# => [{:name=>"key1", :value=>["param_1", "param_2"]},
# {:name=>"key2", :value=>["param_3", "param_4"]},
# {:name=>"key3", :value=>["param_5"]},
# {:name=>"key4", :value=>["param_6"]},
# {:name=>"key5", :value=>["param_7", "param_8"]}]
hsh = {"key1" => ["param_1","param_2"],
"key2" => ["param_3","param_4"], "key3" => "param_5",
"key4" => "param_6","key5" => ["param_7","param_8"]}
hsh.map{|k,v| {"name" => k,"value" => Array(v) }}
# => [{"name"=>"key1", "value"=>["param_1", "param_2"]},
# {"name"=>"key2", "value"=>["param_3", "param_4"]},
# {"name"=>"key3", "value"=>["param_5"]},
# {"name"=>"key4", "value"=>["param_6"]},
# {"name"=>"key5", "value"=>["param_7", "param_8"]}]
I have a Hash which is of the form
{:a => {"aa" => 11,"ab" => 12}, :b => {"ba" => 21,"bb" => 22}}
How do i convert it to the form {:a => [["aa",11],["ab",12]],:b=>[["ba",21],["bb",22]]}
If you want to modify the original hash you can do:
hash.each_pair { |key, value| hash[key] = value.to_a }
From the documentation for Hash#to_a
Converts hsh to a nested array of [
key, value ] arrays.
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
h.to_a #=> [["c", 300], ["a", 100], ["d", 400]]
Here is another way to do this :
hsh = {:a => {"aa" => 11,"ab" => 12}, :b => {"ba" => 21,"bb" => 22}}
hsh.each{|k,v| hsh[k]=*v}
# => {:a=>[["aa", 11], ["ab", 12]], :b=>[["ba", 21], ["bb", 22]]}
hash.collect {|a, b| [a, hash[a].collect {|c,d| [c,d]}] }.collect {|e,f| [e => f]}