How can I get named parameters into a Hash? - ruby

I want to have named parameters to a method so the API is clear to the caller, but the implementation of the method needs the named parameters in a hash. So I have this:
def my_method(required_param, named_param_1: nil, named_param_2: nil)
named_params = {
named_param_1: named_param_1,
named_param_2: named_param_2
}
# do something with the named params
end
This works, but I have to do this in quite a few places, and I would rather have some helper that dynamically gets the named parameters into a hash. I haven't been able to find a way to do this. Any thoughts on how to accomplish this?

def my_method(required_param, named_param_1: nil, named_param_2: nil)
named_params = method(__method__).parameters.each_with_object({}) do |p,h|
h[p[1]] = eval(p[1].to_s) if p[0] == :key
end
p named_params # {:named_param_1=>"hello", :named_param_2=>"world"}
# do something with the named params
end
my_method( 'foo', named_param_1: 'hello', named_param_2: 'world' )

Ruby 2.0 provides no built-in way to get a Hash from keyword arguments.
You must choose between:
Using keyword arguments (as you currently are) and creating a Hash from those values separately, OR...
Using a Hash argument with default values, such as the Ruby v1.9.3 strategies described here.

You could do write your method as below :
def my_method(required_param, named_param_1: nil, named_param_2: nil)
named_params = Hash[method(__method__).parameters.select do |first,last|
[last,eval("#{last}")] if first == :key
end]
end
my_method(12, named_param_1: 11,named_param_2: 12)
# => {:named_param_1=>11, :named_param_2=>12}

Related

How can I create a method that takes a hash (with or without an assigned value) as an argument?

So I am working through test first and am a little stuck. Here is my code so far:
class Dictionary
attr_accessor :entries, :keywords, :item
def initialize
#entries = {}
end
def add(item)
item.each do |words, definition|
#entries[words] = definition
end
end
def keywords
#entries.keys
end
end#class
I am stuck at the rspec test right here:
it 'add keywords (without definition)' do
#d.add('fish')
#d.entries.should == {'fish' => nil}
#d.keywords.should == ['fish']
end
How can I switch my add method around to take either a key/value pair, or just a key with the value set to nil? The first test specifies that the hash is empty when it is created so I cant give it default values there.
One might check the type of the parameter passed to the add method. Whether it’s not an Enumerable, which is apparently a mixin included in Arrays, Hashes etc., just assign it’s value to nil:
def add(item)
case item
when Enumerable
item.each do |words, definition|
#entries[words] = definition
end
else
#entries[item] = nil
end
end
Please note that case uses “case equality” to check argument type.
If you are always passing Strings to the method, you could just have a default value for the second string... Something like the following:
def add(word, definition = nil)
#entries[word] = definition
end
So your code might look something like this:
class Dictionary
attr_accessor :entries, :keywords, :item
def initialize
#entries = {}
end
def add(word, definition = nil)
#entries[word] = definition
end
def keywords
#entries.keys
end
end#class
If you want multiple additions (i.e. add key: "word", with: "many", options: nil), that design might not work for you and you would need to create a solution that would work on the lines of what #mudasobwa suggested. Perhaps:
def add(word, definition = nil)
return #entries[word] = definition unless word.is_a?(Enumerable)
return #entries.update word if word.is_a?(Hash)
raise "What?!"
end
Update, as par request
I updated the method above to allow for words that aren't strings (as you pointed out).
When passing a hash to a method, it is considered as a single parameter.
Key => Value pairs are an implied hash, so when passing a hash to a method, the following are generally the same:
Hash.new.update key: :value
Hash.new.update({key: :value})
Consider the following:
def test(a,b = nil)
puts "a = #{a}"
puts "b = #{b}"
end
test "string"
# => a = string
# => b =
test "string", key: :value, key2: :value2
# => a = string
# => b = {:key=>:value, :key2=>:value2}
test key: :value, key2: :value2, "string"
# Wrong Ruby Syntax due to implied Hash, would raise exception:
# => SyntaxError: (irb):8: syntax error, unexpected '\n', expecting =>
test({key: :value, key2: :value2}, "string")
# correct syntax.
This is why, when you pass add 'fish' => 'aquatic', it's considered only one parameter, a hash - as opposed to add 'fish', 'aquatic' which passes two parameters to the method.
If your method must accept different types of parameters (strings, hashes, numerals, symbols, arrays), you will need to deal with each option in a different way.
This is why #mudasobwa suggested checking the first parameter's type. His solution is pretty decent.
My version is a bit shorter to code, but it runs on the same idea.
def add(word, definition = nil)
return #entries[word] = definition unless word.is_a?(Enumerable)
return #entries.update word if word.is_a?(Hash)
raise "What?!"
end

ruby 2.0 named parameters from a hash

If I have a method in ruby that takes named arguments...
def smoosh(first: nil, second: nil)
first + second
end
Whats the easiest way to pass a hash to that method if the keys match:
params = { first: 'peanut', second: 'butter' }
smoosh(params)
The above produces an argument error.
Update:
It seems like this might be an issue with how Sinatra parameters work.
When I do:
get 'a_sinatra_route' do
hash = params.clone
hash.symbolize_keys!
smoosh(hash)
end
It works fine. It does not work when just passing the params in by themselves. (even though you can access the individual params with the symbol key params[:attr])
Seems to work just fine for me.
2.0.0p0 :007 > def smoosh(first: nil, second: nil)
2.0.0p0 :008?> first + second
2.0.0p0 :009?> end
=> nil
2.0.0p0 :010 > params = { first: 'peanut', second: 'butter' }
=> {:first=>"peanut", :second=>"butter"}
2.0.0p0 :012 > smoosh(params)
=> "peanutbutter"
If your function is using keyword arguments, you'll need to use ** (double splat). The ** (introduced in Ruby 2.0) acts like the original * operator, but can be used with Hashes (see also: another good resource):
def smoosh(first: nil, second: nil)
first + second
end
params = { first: 'double', second: 'splat' }
smoosh(**params)
=> "doublesplat"
It's throwing an ArgumentError because you're passing in one hash to a method that takes two arguments - even though the hash has two key/value pairs, it's still just one argument!
In this situation, you can try:
smoosh(params[:first], params[:second])
To pass in the values.

Ruby Optional Parameters and Multiple Parameters

I am trying to set the first argument to a method as being optional, followed by any number of args. For example:
def dothis(value=0, *args)
The issue I am running into is that it doesn't seem like this is actually possible? When I call dothis("hey", "how are you", "good") I was hoping it would set value to default to 0, but instead it is just making value="hey". Is there any way to accomplish this behavior?
This is not possible directly in Ruby
There are plenty of options though, depending on what you are doing with your extended params, and what the method is intended to do.
Obvious choices are
1) Take named params using hash syntax
def dothis params
value = params[:value] || 0
list_of_stuff = params[:list] || []
Ruby has nice calling convention around this, you don't need to provide the hash {} brackets
dothis :list => ["hey", "how are you", "good"]
2) Move value to the end, and take an array for the first param
def dothis list_of_stuff, value=0
Called like this:
dothis ["hey", "how are you", "good"], 17
3) Use a code block to provide the list
dothis value = 0
list_of_stuff = yield
Called like this
dothis { ["hey", "how are you", "good"] }
4) Ruby 2.0 introduced named hash parameters, which handle a lot of option 1, above for you:
def dothis value: 0, list: []
# Local variables value and list already defined
# and defaulted if necessary
Called same way as (1):
dothis :list => ["hey", "how are you", "good"]
This post is a little bit old, but I want to contribute if someone is looking for the best solution for that. Since ruby 2.0, you can do that easily with named arguments defined with a hash. The syntax is easy and more readable.
def do_this(value:0, args:[])
puts "The default value is still #{value}"
puts "-----------Other arguments are ---------------------"
for i in args
puts i
end
end
do_this(args:[ "hey", "how are you", "good"])
You can also do the same thing with the greedy keyword **args as a hash, like this:
#**args is a greedy keyword
def do_that(value: 0, **args)
puts "The default value is still #{value}"
puts '-----------Other arguments are ---------------------'
args.each_value do |arg|
puts arg
end
end
do_that(arg1: "hey", arg2: "how are you", arg3: "good")
You will need to use named parameters to accomplish this:
def dothis(args)
args = {:value => 0}.merge args
end
dothis(:value => 1, :name => :foo, :age => 23)
# => {:value=>1, :name=>:foo, :age=>23}
dothis(:name => :foo, :age => 23)
# => {:value=>0, :name=>:foo, :age=>23}
by using value=0 you are actually assigning 0 to value. just to retain the value, you can either use the above mentioned solutions or just simply use value everytime you call this method def dothis(value, digit=[*args]).
The default arguments are used when the arguments are not provided.
I came across the similar issue and I got over it by using:
def check(value=0, digit= [*args])
puts "#{value}" + "#{digit}"
end
and simply call check like this:
dothis(value, [1,2,3,4])
your value would be default and other values belong to other argument.

Ruby multiple named arguments

I'm very new to ruby and I'm trying to write a web application using the rails framework. Through reading I've seen methods being called like this:
some_method "first argument", :other_arg => "value1", :other_arg2 => "value2"
Where you can pass an unlimited number of arguments.
How do you create a method in ruby that can be used in this way?
Thanks for the help.
That works because Ruby assumes the values are a Hash if you call the method that way.
Here is how you would define one:
def my_method( value, hash = {})
# value is requred
# hash can really contain any number of key/value pairs
end
And you could call it like this:
my_method('nice', {:first => true, :second => false})
Or
my_method('nice', :first => true, :second => false )
This is actually just a method that has a hash as an argument, below is a code example.
def funcUsingHash(input)
input.each { |k,v|
puts "%s=%s" % [k, v]
}
end
funcUsingHash :a => 1, :b => 2, :c => 3
Find out more about hashes here http://www-users.math.umd.edu/~dcarrera/ruby/0.3/chp_03/hashes.html
Maybe that *args can help you?
def meh(a, *args)
puts a
args.each {|x| y x}
end
Result of this method is
irb(main):005:0> meh(1,2,3,4)
1
--- 2
--- 3
--- 4
=> [2, 3, 4]
But i prefer this method in my scripts.
You can make the last argument be an optional hash to achieve that:
def some_method(x, options = {})
# access options[:other_arg], etc.
end
However, in Ruby 2.0.0, it is generally better to use a new feature called keyword arguments:
def some_method(x, other_arg: "value1", other_arg2: "value2")
# access other_arg, etc.
end
The advantages of using the new syntax instead of using a hash are:
It is less typing to access the optional arguments (e.g. other_arg instead of options[:other_arg]).
It is easy to specify a default value for the optional arguments.
Ruby will automatically detect if an invalid argument name was used by the caller and throw an exception.
One disadvantage of the new syntax is that you cannot (as far as I know) easily send all of the keyword arguments to some other method, because you don't have a hash object that represents them.
Thankfully, the syntax for calling these two types of methods is the same, so you can change from one to the other without breaking good code.

Is there an idiomatic way to specify default values for optional parameters in Ruby?

Is there a more concise and idiomatic way to write the following code, which is used to specify default values for optional parameters (in the params/options hash) to a method?
def initialize(params={})
if params.has_key? :verbose
#verbose = params[:verbose]
else
#verbose = true # this is the default value
end
end
I would love to simplify it to something like this:
def initialize(params={})
#verbose = params[:verbose] or true
end
which almost works, except that you really need to use has_key? :verbose as the condition, instead of just evaluating params[:verbose], in order to cover cases when you want to specify a value of 'false' (i.e. if you want to pass :verbose => false as the argument in this example).
I realize that in this simple example I could easily do:
def initialize(verbose=false)
#verbose = verbose
end
but, in my real code I actually have a bunch of optional parameters (in addition to a few required ones) and I'd like to put the optional ones in the params hash so that I can easily only specify (by name) the few that I want, rather than having to list them all in order (and potentially having to list ones I don't actually want).
A common pattern is to use
def foo(options = {})
options = { :default => :value }.merge(options)
end
You’ll end up with options being a hash containing the passed in values, with the options from your defaults hash as any that weren’t provided.
Ruby 2.0.0 has a new feature keyword arguments
Previously you had to write such code:
def foo(options = {})
options = {bar: 'bar'}.merge(options)
puts "#{options[:bar]} #{options[:buz]}"
end
foo(buz: 'buz') # => 'bar buz'
Now this is much cleaner:
def foo(bar: 'bar', **options)
puts "#{bar}, #{options}"
end
foo(buz: 'buz') # => 'bar buz'
I think you're looking for this
params = { :verbose => true }.merge(params)
Another way to write this, more succinctly, would be
def foo(options = {})
options.reverse_merge! value1: true, value2: 100
end
This set options[:value1] to true (default value) unless options passed in already
contains the key :value1. Same for :value2

Resources