How to generate symbols inside a loop in ruby - ruby-on-rails

I have a section of a web app that passes a hash of values like this for each of the order_items in an order.
Like this:
def example_method
values = {
amount_1: order_items.first.unit_price,
item_name_1: order_items.first.unit_price,
quantity_1: order_items.first.quantity,
amount_2 order_items.second.unit_price,
item_name_2: order_items.second.unit_price,
quantity_2: order_items.second.quantity,
}
end
I'm trying to generate a solution where I have a variable like n = 1 and generate the symbols for each order_item using n like this:
def example_method
values = {
n=1
order_items.each do |item|
:"amount_#{n}" => item.unit_price,
:"item_name_#{n}" => item.name,
:"item_number_#{n}" => item.id,
:"quantity_#{n}" => item.quantity,
n += 1
end
end
I tried this syntax outside of the loop and it works perfectly to generate the symbols using the variable, the problem is that I get a Syntax Error when the code is inside the loop:
How can I achieve this behavior inside my code?
What am I missing?
#edit
Edit for a proposed solution:
values = {
# The rest of static values on the hash...
}
n = 1
items = order_items.each do |item|
{
"amount_#{n}".to_sym => item.unit_price,
"item_name_#{n}".to_sym => item.printer.name,
"item_number_#{n}".to_sym => item.id,
"quantity_#{n}".to_sym => item.quantity,
}
n += 1
end
values.merge(items)

please try this
n=1
order_items.each do |item|
"amount_#{n}".to_sym => item.unit_price,
"item_name_#{n}".to_sym => item.name,
"item_number_#{n}".to_sym => item.id,
"quantity_#{n}".to_sym => item.quantity,
n += 1
end
#edit
items = order_items.each do |item|
"amount_#{n}".to_sym => item.unit_price,
"item_name_#{n}".to_sym => item.name,
"item_number_#{n}".to_sym => item.id,
"quantity_#{n}".to_sym => item.quantity,
n += 1
end
old_hash.merge(items)
maybe you could running this and merge with outside hash
edit 2
you can install gem 'pry' to a better debug solution. For more info check this videos RailsCast Pry Debug
values = {
# The rest of static values on the hash...
}
n = 1
items = order_items.each do |item|
{
"amount_#{n}".to_sym => item.unit_price,
"item_name_#{n}".to_sym => item.printer.name,
"item_number_#{n}".to_sym => item.id,
"quantity_#{n}".to_sym => item.quantity,
}
n += 1
end
binding.pry # this will able a ruby shell prompt and you can execute your code and analyse the values
values.class # in pry prompt
items.class # in pry prompt
values.merge(items)
#edit 3
#items = {}
order_items.each do |item|
#items = {
"amount_#{n}".to_sym => item.unit_price,
"item_name_#{n}".to_sym => item.printer.name,
"item_number_#{n}".to_sym => item.id,
"quantity_#{n}".to_sym => item.quantity}
values = values.merge(#items)
n += 1
end

You can't use the new symbol syntax with interpolation, it has to be the old one. Also, mutating an index inside the loop is a bad practice in general in Ruby, use each_with_index instead:
order_items.each_with_index do |item, index|
{
:"amount_#{index}" => item.unit_price,
:"item_name_#{index}" => item.name,
:"item_number_#{index}" => item.id,
:"quantity_#{index}" => item.quantity,
}
end

Syntax as such
amount_1: 1
is not valid as a key value needs to be wrapped in {} to be a hash or when passed as arguments to a method call.
Also since you're interpolating a string you can do the following
:"amount_#{n}" => 1
If what you're aiming to do is return an array of hashes then you want to do the following
n=0
order_items.map do |item|
n += 1
{
:"amount_#{n}" => item.unit_price,
:"item_name_#{n}" => item.name,
:"item_number_#{n}" => item.id,
:"quantity_#{n}" => item.quantity,
}
end

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 ....

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.

Rails get from view table column name and value of select

I now, that question could be non good for so, but need help:
In my rails haml view i have such code:
%table.table.table-striped
= form_tag :admin_other_price_upload do
%tr
- #csv.first.length.times do |n|
%th
= n + 1
%br/
=select_tag "s"+(n+1).to_s, options_for_select([["Брэнд", "Brand"], ["Артикул","Article"], ["Наименование","Descr"], ["Цена","Price"], ["Количество","Quantity"], ["Дополнительно","Other"], ["Поле кроссов","Cross"]]), :prompt => 'Все', :id => "select-value"
*********************************
so as you can see i'm setting to all select's name like s(n+1) and value one from list. But how can i get them both in controller method? I need it becouse i have dynamic table. I will explain it on example:
So i have table with select's
name = s1 (value = Brand) | name = s2 (value = Price)
so i need in controller to get not only that s1 => Brand, but also get this 1 from s1
So if param look's like
[
s1 => {Brand}
]
I need to get for my calculation s1 not value, but s1 as string (simple i need to find in params, which value has Brand and select it as a value)
So for Brand i need to select s1, and set as s1 value s1, how could i do it?
I may have understood you, but not sure.
# let's say your params hash is like:
params = { :action => 'show', :controller => 'articles',
:s1 => 'Article', :s2 => 'Brand', :s3 => 'Price', ... }
brand_param = params.select{ |key, value| value == 'Brand' }
# => { :s2 => 'Brand' }
which_s_is_brand = brand_param.keys.first.to_s
# => 's2'

Search ignoring certain blank parameters

I have a simple search action that has 3 parameters and a where method to search a model. If I search and some of the parameters are nil, it will not return the records I want. I want it to search the database by only using the parameters that are not nil/blank. So if only one category is entered and sent in the parameters, I want my controller to ignore the other two parameters. However, if the other parameters are present, I want them to be included in the search.
I've tried many approaches but I can't get it to work properly. Here's my code.
hash = []
cat = :category_id => params[:category_id]
col = :color_id => params[:color_id]
brand = :brand_id => params[:brand_id]
if params[:category_id].present?
hash += cat
end
if params[:color_id].present?
hash += col
end
if params[:brand_id].present?
hash += brand
end
#results = Piece.where(hash).preload(:item).preload(:user).group(:item_id).paginate(:page => params[:page], :per_page => 9)
I've put the variables into strings and hashs, called to_a, joined them with (","). Nothing works.
Thanks
Try this code.
criteria = { :category_id => params[:category_id], :color_id => params[:color_id],
:brand_id => params[:brand_id] }.select { |key,value| value.present? }
#results = Piece.where(criteria).preload(:item).preload(:user).group(:item_id).
paginate(:page => params[:page], :per_page => 9)

Hash - nested traversal - Ruby - (Any one know this)

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, "_", "-")

Resources