I have a Ruby hash that I need to convert to another format.
Considering that the 'array' size is unknown/unlimited, how would you 'flatten' this hash into the desired format?
Original Hash
{
:parameters => {
:'userResponse.objectInstanceType' => 'QuesAnsResponse',
:'userResponse.quesAnsDetailArray' => {
:'0' => {
'.answer'=> 'Texas'
},
:'1' => {
'.answer' => 'w3schools'
}
}
}
}
Desired Format:
{
:parameters => {
:'userResponse.objectInstanceType' => 'QuestionAnsResponse',
:'userResponse.quesAnsDetailArray[0].answer' => 'Texas',
:'userResponse.quesAnsDetailArray[1].answer' => 'w3schools'
}
}
If you can make a few assumptions about the structure of the input i.e. that it has the numbered responses inside a "userResponse.quesAnsDetailArray" section then you could do something along these lines:
new_hash = { 'parameters' =>
{ 'userResponse.objectInstanceType' => 'QuestionAnsResponse' } }
hash['parameters']['userResponse.quesAnsDetailArray'].each_pair do |index, details|
details.each_pair do |field, value|
new_hash['parameters']["userResponse.quesAnsDetailArray[#{index}]#{field}"] = value
end
end
I have assumed the hash is as follows:
h = {
'parameters'=> {
'userResponse.objectInstanceType'=> 'QuesAnsResponse',
'userResponse.quesAnsDetailArray'=> {
'0'=> {
'.answer'=> '',
'.answerFieldType'=> 'text',
'.isRequired'=> 'true',
'.metaData'=> 'QUESTION_1',
'.questionFieldType'=> 'label',
'.question'=> 'What is the name of your state?'
},
'1'=> {
'.answer'=> '',
'.answerFieldType'=> 'text',
'.isRequired'=> 'true',
'.metaData'=> 'QUESTION_2',
'.questionFieldType'=> 'label',
'.question'=> 'What is the name of your first school'
}
}
}
}
and would convert it to the desired format as follows:
{ 'parameters'=> Hash[[
['userResponse.objectInstanceType',
h['parameters']['userResponse.objectInstanceType']],
*h['parameters']['userResponse.quesAnsDetailArray'].
flat_map { |ndx,f|
["userResponse.quesAnsDetailArray[#{ndx}]"].product(f.to_a) }.
map { |prefix,(suffix,value)| [prefix+suffix, value] } ]]
}
#=> {"parameters"=>
{"userResponse.objectInstanceType"=>"QuesAnsResponse",
"userResponse.quesAnsDetailArray[0].answer"=>"",
"userResponse.quesAnsDetailArray[0].answerFieldType"=>"text",
"userResponse.quesAnsDetailArray[0].isRequired"=>"true",
"userResponse.quesAnsDetailArray[0].metaData"=>"QUESTION_1",
"userResponse.quesAnsDetailArray[0].questionFieldType"=>"label",
"userResponse.quesAnsDetailArray[0].question"=>
"What is the name of your state?",
"userResponse.quesAnsDetailArray[1].answer"=>"",
"userResponse.quesAnsDetailArray[1].answerFieldType"=>"text",
"userResponse.quesAnsDetailArray[1].isRequired"=>"true",
"userResponse.quesAnsDetailArray[1].metaData"=>"QUESTION_2",
"userResponse.quesAnsDetailArray[1].questionFieldType"=>"label",
"userResponse.quesAnsDetailArray[1].question"=>
"What is the name of your first school"}}
Related
I have this:
produtos = LineItem.select('codigosku, quantity').where("cart_id = #{session[:cart_id] } ")
I need to insert the result of this select (produto variable), here:
message = Hash.new
message = {
"tem:carrinho" => {"gpa:CEP" => params[:cep],
"gpa:CNPJ" => 'doc',
"gpa:IdCampanha" => 1111,
"gpa:Produtos" => {"gpa:DadosListaProdutoCarrinhoDTO" =>
{
HERE! VALUES OF "PRODUTOS VARIABLE"
}
}
}
}
How can I do this?
Thanks in advance!
create your array:
line_items_array = line_items.map{|li| li.attributes }
Then insert the array within your hash.
like in apneadiving example, use map to create an array from the produtos data; use attributes to return all data (it is a hash) from your selected data
message = {
"tem:carrinho" => {
"gpa:CEP" => params[:cep],
"gpa:CNPJ" => 'doc',
"gpa:IdCampanha" => 1111,
"gpa:Produtos" => {
"gpa:DadosListaProdutoCarrinhoDTO" => produtos.map { |item| item.attributes }
}
}
}
or if you need to be more specific about the keys in the produtos and append it after initialization
# initialize the Produtos to nil
message = {
"tem:carrinho" => {
"gpa:CEP" => params[:cep],
"gpa:CNPJ" => 'doc',
"gpa:IdCampanha" => 1111,
"gpa:Produtos" => nil
}
}
# build an array of DadosListaProdutoCarrinhoDTO
list = produtos.map do |item|
{
"gpa:DadosListaProdutoCarrinhoDTO" => {
"codigosku" => item.codigosku,
"quantity" => item.quantity
}
}
end
# set the Produtos key to an array of DadosListaProdutoCarrinhoDTO
message["tem:carrinho"].merge!({ "gpa:Produtos" => list })
I got some yml config files with a deep, different structures and I want to extract passwords(that are located on different levels) and store them in a yml file outside a git repo. Let me show an example
config1.yml
a:
b:
c: 1
password: secret
...
d: 2
...
I wish to replace secret with '' and extract a pass to a different yml file that will look like:
config1_passwords.yml
a:
b:
password: secret
Is there any way to merge config1.yml without passwords with config1_passwords.yml to get a correct config structure?
So basically in terms of hashes(parsed ymls) I want to do following:
{ :a => { :b => { :c => 1, :password => '' }, :d => 2 } }
{ :a => { :b => { :password => 'secret' } } }
# =>
{ :a => { :b => { :c => 1, :password => 'secret' }, :d => 2 } }
Thanks for suggestions
EDITED
another example
{ :a => { :b => { :c => 1, :d => { :password1 => '' }, :password2 => '' } } }
{ :a => { :b => { :d => { :password => 'secret' }, :password2 => 'secret2' } } }
# =>
{ :a => { :b => { :c => 1, :d => { :password => 'secret' }, :password2 => 'secret2' } } }
Rails 3 has a deep_merge, which does exactly what you want.
a = { :a => { :b => { :c => 1, :d => { :password1 => '' }, :password2 => '' } } }
b = { :a => { :b => { :d => { :password1 => 'secret' }, :password2 => 'secret2' } } }
a.deep_merge(b)
# -> {:a=> {:b=> {:c=>1, :d=>{:password1=>"secret"}, :password2=>"secret2"}}}
Note: I changed a[:a][:b][:d] to contain :password1 instead of :password.
Don't think this can be done using Ruby one-liners. But a simple recursive function might do
def recurse_merge_password! conf_hash, pw_hash
pw_hash.keys.each do |k|
next unless conf_hash.has_key?(k)
case
when k == :password
conf_hash[:password] = pw_hash[:password]
when conf_hash[k].is_a?(Hash) && pw_hash[k].is_a?(Hash)
recurse_merge_password! conf_hash[k], pw_hash[k]
end
end
end
h1 = { :a => { :b => { :c => 1, :password => '' }, :d => 2 } }
h2 = { :a => { :b => { :password => "secret" } } }
recurse_merge_password! h1, h2
puts h1.inspect
=> {:a=>{:b=>{:c=>1, :password=>"secret"}, :d=>2}}
If you have arrays and other structures that you may need to traverse, it is up to you to improve on this. Note I made it modify the config in place.
It appears you want some sort of Hash deep merge. It's available in ActiveSupport (part of Rails):
# You can omit this require statement if you're running Rails.
require "active_support/core_ext/hash/deep_merge"
a = { a: { b: { c: 1, d: { password1: "" }, password2: "" } } }
b = { a: { b: { d: { password1: "secret" }, password2: "secret2" } } }
a.deep_merge(b)
#=> { a: { b: { c: 1, d: { password1: "secret"}, password2: "secret2" } } }
If you don't want to depend on ActiveSupport, take a look at the implementation.
Is somebody able to suggest me a way to do this ? ( all is in the subject :D )
what I want is using a "path" and tranform it as a suit of sub key,
e.g. : I have that params: path = "earth/animal/human/men/young/" value = "martin"
and I want :
`Global_hash = { earth => { human => { men => { young => "martin"
}
}
}
}`
path = "earth/animal/human/men/old/" value = "John"
and I want :
Global_hash = { earth => { human => { men => { young => "martin",
old => "John"
}
}
}
}
add an other
path = "earth/animal/human/women/old/" value = "Eve"
and I want :
`Global_hash = { earth => { human => { men => { young => "martin",
old => "John"
},
women => { old => "Eve"
}
}
}
}
`
The final goal is a way to produce yml file with 2 parameters :
the path and the value
the exemple produces :
`
earth:
animal:
human:
men:
young: "martin"
old: "John"
women:
old: "Eve"
`
it will allow us to have a yml file with all object sort by sections thanks to their path.
Thanks per advance
path = 'earth/animal/human/men/young/'
value = 'martin'
path.split('/').reverse.reduce(value){ |r, e| {e.to_sym => r} }
Functional recursive approach:
def insert(hash, path, value)
head, *tail = path
if tail.empty?
hash.merge(head => value)
else
h = insert(hash[head] || {}, tail, value)
hash.merge(head => hash.has_key?(head) ? hash[head].merge(h) : h)
end
end
h1 = insert({}, "animal/human/women/old".split("/"), "Eve")
# {"animal"=>{"human"=>{"women"=>{"old"=>"Eve"}}}}
h2 = insert(h1, "animal/human/men/old".split("/"), "Adam")
# {"animal"=>{"human"=>{"women"=>{"old"=>"Eve"},
# "men"=>{"old"=>"Adam"}}}}
h3 = insert(h2, "animal/chimpanzee/smart".split("/"), "Caesar")
# {"animal"=>{"human"=>{"women"=>{"old"=>"Eve"},
# "men"=>{"old"=>"Adam"}},
# "chimpanzee"=>{"smart"=>"Caesar"}}}
I have that deep Hash of hashes:
my_hash = {
:category_1 => {
:solution_1 => { :order => 1 },
:solution_2 => { :order => 2 }
},
:category_2 => {
:solution_3 => { :order => 3 },
:solution_4 => { :order => 4 }
}
}
I want to sort :solution_* hashes under :category_* hashes by key :order. Any suggestions?
(fixed)
Let's say you have the following hash of people to ages:
people = {
:fred => { :name => "Fred", :age => 23 },
:joan => { :name => "Joan", :age => 18 },
:pete => { :name => "Pete", :age => 54 }
}
use sort_by to get where we want to go:
people.sort_by { |k, v| v[:age] }
# => [[:joan, {:name=>"Joan", :age=>18}],
[:fred, {:name=>"Fred", :age=>23}],
[:pete, {:name=>"Pete", :age=>54}]]
Ok, you didn't specify your question, so I'm assuming you want one layer removed. I changed the starting hash a bit to actually see if the sorting works:
my_hash = {
:category_1 => {
:solution_1 => { :order => 2 },
:solution_2 => { :order => 3 }
},
:category_2 => {
:solution_3 => { :order => 4 },
:solution_4 => { :order => 1 }
}
}
Hash[my_hash.inject({}) { |h, (k, v)| h.merge(v) }.sort_by { |k,v| v[:order] }]
#=> {:solution_4=>{:order=>1}, :solution_1=>{:order=>2}, :solution_2=>{:order=>3}, :solution_3=>{:order=>4}}
EDIT:
Taking into account your clarification (and still starting from the modified unsorted hash I posted above):
sorted = my_hash.inject({}) do |h, (k, v)|
h[k] = Hash[v.sort_by { |k1, v1| v1[:order] }]
h
end
#=> {:category_1=>{:solution_1=>{:order=>2}, :solution_2=>{:order=>3}}, :category_2=>{:solution_4=>{:order=>1}, :solution_3=>{:order=>4}}}
I am using Ruby on Rails 3 and I would like to "trasform" the following array so that I can use my custom logic to access its data.
This is the original array from which I have to build a new one
[
{
"account" => {
"id" => 45,
"name" => "Test_name",
"..." => ..."
}
},
{
"other" => {
"sub_other" => {...}
}
}
]
I would like to trasform the above array so that I can do in my controller something like
array_name[45]
# => {
"name" => "Test_name",
"..." => ..."
}
but only for the account hashs. The other hash should remain untouched.
How can I proceed to build the new array?
If I understand your requirements correctly, I think you are better off constructing a hash from account id to account data. Perhaps something like this will work:
arr = [
{
"account" => {
"id" => 45,
"name" => "Test_name",
"..." => "..."
}
},
{
"other" => {
"sub_other" => "..."
}
}
]
account_hashes = arr.select {|item| item.keys.first == "account"}
answer = account_hashes.inject({}) do |acc, item|
acc[item["account"].delete("id")] = item["account"]
acc
end