I have turned, have turned a hash to params using the to_query method.
How to turn it back into a hash?
I have tried this:
require 'rack'
#tester = Rack::Utils.parse_nested_query(params[:search])
In view: <%= #tester.class %>
Which gives NilClass.
The parameters are:
"search"=>"fields%5B%5D=exhb_0&fields%5B%5D=exh0_1&fields%5B%5
D=t_g_a&fields%5B%5D=hp_1&fields%5B%5D=s1&fields%5B%5D=overflade_0&railing%5B%5D
=A-3&railing_m=0&type%5B%5D=ltrappa&wood%5B%5D=wood_6"
This is a duplicate of this questions: Just use:
Rack::Utils.parse_query(my_query_string)
In order to decode the line in your example, be sure to unescape the string first:
require 'rack'
my_string = 'fields%5B%5D=exhb_0&fields%5B%5D=exh0_1&fields%5B%5D=t_g_a&fields%5B%5D=hp_1&fields%5B%5D=s1&fields%5B%5D=overflade_0&railing%5B%5D=A-3&railing_m=0&type%5B%5D=ltrappa&wood%5B%5D=wood_6'
unescaped_string = URI.unescape(my_string)
# => "fields[]=exhb_0&fields[]=exh0_1&fields[]=t_g_a&fields[]=hp_1&fields[]=s1&fields[]=overflade_0&railing[]=A-3&railing_m=0&type[]=ltrappa&wood[]=wood_6"
params_hash = Rack::Utils.parse_query(unescaped_string)
# => {"fields[]"=>["exhb_0", "exh0_1", "t_g_a", "hp_1", "s1", "overflade_0"], "railing[]"=>"A-3", "railing_m"=>"0", "type[]"=>"ltrappa", "wood[]"=>"wood_6"}
Related
I have an object and want to access to some data but I have an error.
This is the object:
list = [{"id"=>0,"title"=>"Purple Rain"}, {"id"=>1,"title"=>"Life is a flower"},]
With binding.pry, i tried to access to the title of the first object by:
list.first.title
Can you tell me why it doesn't work? If i do "list.first", it will show the first object without any problem but when i want to access to only one data, i got this error:
NoMethodError: undefined method `title' for #<Hash:0x...
Thanks for your help
Hash doesn't have dot syntax. OpenStructs do. If you want to use dot syntax, then you can convert to openstruct if you want. But using what Sebastian suggested is fine.
list
# => [{"id"=>0, "title"=>"Purple Rain"}, {"id"=>1, "title"=>"Life is a flower"}]
list.first["title"]
# => "Purple Rain"
require 'ostruct'
# => true
obj = OpenStruct.new(list.first)
# => #<OpenStruct id=0, title="Purple Rain">
obj.title
# => "Purple Rain"
I'm not getting what I want from my regex.
I'm looking thru #my_string which contains:
"Black Multi/Wide Calf":[{"large":"http://ecx.images-willy.com/images
/I/41suNF66r4L.jpg","variant":"MAIN","hiRes":"http://ecx.images-willy.com
/images/I/51knTtAU6mL._UL1000_.jpg","thumb":"http://ecx.images-willy.com
/images/I/41suNF66r4L._US40_.jpg","main":{"http://ecx.images-willy.com/images
/I/51knTtAU6mL._UY500_.jpg":["500","500""]}}],"Dark Brown":
[{"large":"http://ecx.images......
And I have a variable which is:
#color = "Black Multi"
And my regex looks like:
/^#{#color}(.*)\d+(}\])$/i.match(#my_string)
I want the string that starts with "Black Multi" and ends with }]:
Black Multi/Wide Calf":[{"large":"http://ecx.images-willy.com/images
/I/41suNF66r4L.jpg","variant":"MAIN","hiRes":"http://ecx.images-willy.com
/images/I/51knTtAU6mL._UL1000_.jpg","thumb":"http://ecx.images-willy.com
/images/I/41suNF66r4L._US40_.jpg","main":{"http://ecx.images-willy.com/images
/I/51knTtAU6mL._UY500_.jpg":["500","500""]}}]
I'm getting nil with what I have. where did I jack this up?
It looks like your string is a JSON-encoded object. Don't try to parse it using regex. Instead parse it using a JSON parser, then access its contents like normal.
require 'json'
my_string = '{"Black Multi/Wide Calf":[{"large":"http://ecx.images-willy.com/images/I/41suNF66r4L.jpg","variant":"MAIN","hiRes":"http://ecx.images-willy.com/images/I/51knTtAU6mL._UL1000_.jpg","thumb":"http://ecx.images-willy.com/images/I/41suNF66r4L._US40_.jpg","main":{"http://ecx.images-willy.com/images/I/51knTtAU6mL._UY500_.jpg":["500","500"]}}]}'
obj = JSON[my_string]
# => {"Black Multi/Wide Calf"=>
# [{"large"=>"http://ecx.images-willy.com/images/I/41suNF66r4L.jpg",
# "variant"=>"MAIN",
# "hiRes"=>
# "http://ecx.images-willy.com/images/I/51knTtAU6mL._UL1000_.jpg",
# "thumb"=>
# "http://ecx.images-willy.com/images/I/41suNF66r4L._US40_.jpg",
# "main"=>
# {"http://ecx.images-willy.com/images/I/51knTtAU6mL._UY500_.jpg"=>
# ["500", "500"]}}]}
Because it's now a regular object, in this case a hash, it's easy to access its key/value pairs:
obj["Black Multi/Wide Calf"] # => [{"large"=>"http://ecx.images-willy.com/images/I/41suNF66r4L.jpg", "variant"=>"MAIN", "hiRes"=>"http://ecx.images-willy.com/images/I/51knTtAU6mL._UL1000_.jpg", "thumb"=>"http://ecx.images-willy.com/images/I/41suNF66r4L._US40_.jpg", "main"=>{"http://ecx.images-willy.com/images/I/51knTtAU6mL._UY500_.jpg"=>["500", "500"]}}]
And it's easy to drill down:
obj["Black Multi/Wide Calf"][0]['large'] # => "http://ecx.images-willy.com/images/I/41suNF66r4L.jpg"
You need to add the "multiline" flag (/m) to the regex:
str[/Black Multi.*?\}\]/m]
#=> "Black Multi/Wide Calf\"......\"500\"\"]}}]"
This is my code:
#!/usr/bin/env ruby
# OptionParser
require 'optparse'
options = {}
optparse = OptionParser.new do|opts|
opts.banner = '...'
# This option inputs ...
options[:Lap1] = []
opts.on('-1', '--Lap1 filepath1,width1,height1,a1,first1,last1', String, '...') do|l1|
options[:Lap1] = l1.split(',')
end
end
optparse.parse!
My goal is to have an array of the separate inputs separated by commas. However this code only outputs the first variable $filepath1.
The output of:
puts(options[:Lap1])
and
puts(options[:Lap1][0]
is just the the first variable filepath1.
puts(options[:Lap1][1])
is nil, when it should be the variable width1.
Any suggestions or potential fixes would be helpful, thank you.
You need to call the parse! method on your OptionParser object after declaring all the options.
optparse.parse!
I'd write that code a bit differently:
require 'optparse'
require 'pp'
options = {
:Lap1 => []
}
optparse = OptionParser.new do |opts|
opts.banner = '...'
# This option inputs ...
opts.on(
'-1', '--Lap1 filepath1,width1,height1,a1,first1,last1',
Array,
'...'
) { |l1| options[:Lap1] = l1 }
end.parse!
pp options
Running that at the command-line:
ruby test.rb -1 filepath1,width1,height1,a1,first1,last1
Results in:
{:Lap1=>["filepath1", "width1", "height1", "a1", "first1", "last1"]}
Testing using --Lap1 results in:
ruby test.rb --Lap1 filepath1,width1,height1,a1,first1,last1
And:
{:Lap1=>["filepath1", "width1", "height1", "a1", "first1", "last1"]}
In my opinion, you should set your defaults when you define your options hash.
options = {
:Lap1 => []
}
Also, notice the use of Array instead of String. OptionParser will automatically split a comma-delimited string into an array of individual elements if you use Array, saving you that split step, and avoiding a bit of confusing code. See the documentation for OptionParser's make_switch method for more information.
I have a hash mapping in my yaml file as below. How Can I iterate through it in simple ruby script? I would like to store the key in a variable and value in another variable in my ruby program during the iteration.
source_and_target_cols_map:
-
com_id: community_id
report_dt: note_date
sitesection: site_section
visitor_cnt: visitors
visit_cnt: visits
view_cnt: views
new_visitor_cnt: new_visitors
the way i am getting the data from the yaml file is below:
#!/usr/bin/env ruby
require 'yaml'
config_options = YAML.load_file(file_name)
#source_and_target_cols_map = config_options['source_and_target_cols_map']
puts #source_and_target_cols_map
The YAML.load_file method should return a ruby hash, so you can iterate over it in the same way you would normally, using the each method:
require 'yaml'
config_options = YAML.load_file(file_name)
config_options.each do |key, value|
# do whatever you want with key and value here
end
As per your yaml file it you should get the below Hash from the line config_options = YAML.load_file(file_name)
config_options = { 'source_and_target_cols_map' =>
[ { 'com_id' => 'community_id',
'report_dt' => 'note_date',
'sitesection' => 'site_section',
'visitor_cnt' => 'visitors',
'visit_cnt' => 'visits',
'view_cnt' => 'views',
'new_visitor_cnt' => 'new_visitors' }
]}
Then to iterate through you can take the below approach:
config_options['source_and_target_cols_map'][0].each {|k,v| key = k,value = v}
This question already has answers here:
How to avoid NoMethodError for nil elements when accessing nested hashes? [duplicate]
(4 answers)
Closed 7 years ago.
In Rails we can do the following in case a value doesn't exist to avoid an error:
#myvar = #comment.try(:body)
What is the equivalent when I'm digging deep into a hash and don't want to get an error?
#myvar = session[:comments][#comment.id]["temp_value"]
# [:comments] may or may not exist here
In the above case, session[:comments]try[#comment.id] doesn't work. What would?
You forgot to put a . before the try:
#myvar = session[:comments].try(:[], #comment.id)
since [] is the name of the method when you do [#comment.id].
The announcement of Ruby 2.3.0-preview1 includes an introduction of Safe navigation operator.
A safe navigation operator, which already exists in C#, Groovy, and
Swift, is introduced to ease nil handling as obj&.foo. Array#dig and
Hash#dig are also added.
This means as of 2.3 below code
account.try(:owner).try(:address)
can be rewritten to
account&.owner&.address
However, one should be careful that & is not a drop in replacement of #try. Take a look at this example:
> params = nil
nil
> params&.country
nil
> params = OpenStruct.new(country: "Australia")
#<OpenStruct country="Australia">
> params&.country
"Australia"
> params&.country&.name
NoMethodError: undefined method `name' for "Australia":String
from (pry):38:in `<main>'
> params.try(:country).try(:name)
nil
It is also including a similar sort of way: Array#dig and Hash#dig. So now this
city = params.fetch(:[], :country).try(:[], :state).try(:[], :city)
can be rewritten to
city = params.dig(:country, :state, :city)
Again, #dig is not replicating #try's behaviour. So be careful with returning values. If params[:country] returns, for example, an Integer, TypeError: Integer does not have #dig method will be raised.
The most beautiful solution is an old answer by Mladen Jablanović, as it lets you to dig in the hash deeper than you could with using direct .try() calls, if you want the code still look nice:
class Hash
def get_deep(*fields)
fields.inject(self) {|acc,e| acc[e] if acc}
end
end
You should be careful with various objects (especially params), because Strings and Arrays also respond to :[], but the returned value may not be what you want, and Array raises exception for Strings or Symbols used as indexes.
That is the reason why in the suggested form of this method (below) the (usually ugly) test for .is_a?(Hash) is used instead of (usually better) .respond_to?(:[]):
class Hash
def get_deep(*fields)
fields.inject(self) {|acc,e| acc[e] if acc.is_a?(Hash)}
end
end
a_hash = {:one => {:two => {:three => "asd"}, :arr => [1,2,3]}}
puts a_hash.get_deep(:one, :two ).inspect # => {:three=>"asd"}
puts a_hash.get_deep(:one, :two, :three ).inspect # => "asd"
puts a_hash.get_deep(:one, :two, :three, :four).inspect # => nil
puts a_hash.get_deep(:one, :arr ).inspect # => [1,2,3]
puts a_hash.get_deep(:one, :arr, :too_deep ).inspect # => nil
The last example would raise an exception: "Symbol as array index (TypeError)" if it was not guarded by this ugly "is_a?(Hash)".
The proper use of try with a hash is #sesion.try(:[], :comments).
#session.try(:[], :comments).try(:[], commend.id).try(:[], 'temp_value')
Update: As of Ruby 2.3 use #dig
Most objects that respond to [] expect an Integer argument, with Hash being an exception that will accept any object (such as strings or symbols).
The following is a slightly more robust version of Arsen7's answer that supports nested Array, Hash, as well as any other objects that expect an Integer passed to [].
It's not fool proof, as someone may have created an object that implements [] and does not accept an Integer argument. However, this solution works great in the common case e.g. pulling nested values from JSON (which has both Hash and Array):
class Hash
def get_deep(*fields)
fields.inject(self) { |acc, e| acc[e] if acc.is_a?(Hash) || (e.is_a?(Integer) && acc.respond_to?(:[])) }
end
end
It can be used the same as Arsen7's solution but also supports arrays e.g.
json = { 'users' => [ { 'name' => { 'first_name' => 'Frank'} }, { 'name' => { 'first_name' => 'Bob' } } ] }
json.get_deep 'users', 1, 'name', 'first_name' # Pulls out 'Bob'
say you want to find params[:user][:email] but it's not sure whether user is there in params or not. Then-
you can try:
params[:user].try(:[], :email)
It will return either nil(if user is not there or email is not there in user) or otherwise the value of email in user.
As of Ruby 2.3 this gets a little easier. Instead of having to nest try statements or define your own method you can now use Hash#dig (documentation).
h = { foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) #=> 1
h.dig(:foo, :zot) #=> nil
Or in the example above:
session.dig(:comments, #comment.id, "temp_value")
This has the added benefit of being more like try than some of the examples above. If any of the arguments lead to the hash returning nil then it will respond nil.
#myvar = session.fetch(:comments, {}).fetch(#comment.id, {})["temp_value"]
From Ruby 2.0, you can do:
#myvar = session[:comments].to_h[#comment.id].to_h["temp_value"]
From Ruby 2.3, you can do:
#myvar = session.dig(:comments, #comment.id, "temp_value")
Another approach:
#myvar = session[:comments][#comment.id]["temp_value"] rescue nil
This might also be consider a bit dangerous because it can hide too much, personally I like it.
If you want more control, you may consider something like:
def handle # just an example name, use what speaks to you
raise $! unless $!.kind_of? NoMethodError # Do whatever checks or
# reporting you want
end
# then you may use
#myvar = session[:comments][#comment.id]["temp_value"] rescue handle
When you do this:
myhash[:one][:two][:three]
You're just chaining a bunch of calls to a "[]" method, an the error occurs if myhash[:one] returns nil, because nil doesn't have a [] method. So, one simple and rather hacky way is to add a [] method to Niclass, which returns nil: i would set this up in a rails app as follows:
Add the method:
#in lib/ruby_extensions.rb
class NilClass
def [](*args)
nil
end
end
Require the file:
#in config/initializers/app_environment.rb
require 'ruby_extensions'
Now you can call nested hashes without fear: i'm demonstrating in the console here:
>> hash = {:foo => "bar"}
=> {:foo=>"bar"}
>> hash[:foo]
=> "bar"
>> hash[:doo]
=> nil
>> hash[:doo][:too]
=> nil
Andrew's answer didn't work for me when I tried this again recently. Maybe something has changed?
#myvar = session[:comments].try('[]', #comment.id)
The '[]' is in quotes instead of a symbol :[]
Try to use
#myvar = session[:comments][#comment.id]["temp_value"] if session[:comments]