Dynamic parameters using variable as key - ruby

Lets say I have a method foo:
def foo(options = {})
...
end
I want to pass a variable as the key.
fooKey = "option1"
foo(fooKey: "bar1", option2: 'bar2')
Is this possible?

Just use the hashrocket syntax
foo(fooKey => 'bar1', :option2 => 'bar2')

Related

Function Parameters to Hash Symbols for Rendering Partials

I have a lot of Render Helper functions like
def generic_form_datetime_field(f,attribute_name, my_label ,selected_model_instance)
render(:partial => 'common_partials/generic_form/datetime_field',
:locals => {:f => f,
:attribute_name => attribute_name,
:my_label => my_label,
:selected_model_instance => selected_model_instance
})
end
notice the local field is full of duplicate keys and values from function parameters. What is the best practice around this? Is there a way to get the parameters from the method and put them as keys for the hash?
taking hash arguments
You can use a few different syntaxes to pull in named arguments as a hash. One is using the "double splat":
def func(**args)
puts args
end
func(a: 'aval', b: 3) # prints {:a=>"aval", :b=>3}
Another syntax you will see is:
def func(opts={})
puts opts
end
func(a: 'aval', b: 3) # prints: {:a=>"aval", :b=>3}
More info can be found in this blog post.
filtering the hash
If you want to be defensive, you can use a function that filters collections in general: select. Here's an example of how to only take values from a specified whitelist for hash keys:
h = {a: 'a', b: 'b', c: 'c'}
whitelist = [:a, :c]
h.select { |k, _| whitelist.include?(k) }
# result: {:a=>"a", :c=>"c"}

Convert string to symbol/keyword

We can convert strings to symbols in the following way:
"string_to_symbol".to_sym
# => :string_to_symbol
How do I convert strings in the new way of defining keywords? Expected outcome:
# => string_to_symbol:
I call keywords dynamically, and I end up using => to assign a value to it. I prefer not to do it that way to keep my code consistent.
No, there isn't.
It's important to note that these two lines do exactly the same:
{ foo: 'bar' } #=> {:foo=>"bar"}
{ :foo => 'bar' } #=> {:foo=>"bar"}
This is because the first form is only Syntactic sugar for creating a Hash using a Symbol as the Key. (and not "the new way of defining keyword in ruby")
If you want to use other types as the key, you still have to use the "hashrocket" (=>):
{ 'key' => 'val' } #=> {"key"=>"val"}
{ 0 => 'val' } #=> {0=>"val"}
Edit:
As #sawa noted in the comments, the question is about passing keyword arguments, not Hashes. Which is technically correct, but boils down to exactly the same (as long as it's Hashes with Symbols as keys:
def foo(bar: 'baz')
puts bar
end
h = {
:bar => 'Ello!'
}
foo(h)
# >> Ello!

How to access a symbol hash key using a variable in Ruby

I have an array of hashes to write a generic checker for, so I want to pass in the name of a key to be checked. The hash was defined with keys with symbols (colon prefixes). I can't figure out how to use the variable as a key properly. Even though the key exists in the hash, using the variable to access it results in nil.
In IRB I do this:
>> family = { 'husband' => "Homer", 'wife' => "Marge" }
=> {"husband"=>"Homer", "wife"=>"Marge"}
>> somevar = "husband"
=> "husband"
>> family[somevar]
=> "Homer"
>> another_family = { :husband => "Fred", :wife => "Wilma" }
=> {:husband=>"Fred", :wife=>"Wilma"}
>> another_family[somevar]
=> nil
>>
How do I access the hash key through a variable? Perhaps another way to ask is, how do I coerce the variable to a symbol?
You want to convert your string to a symbol first:
another_family[somevar.to_sym]
If you want to not have to worry about if your hash is symbol or string, simply convert it to symbolized keys
see: How do I convert a Ruby hash so that all of its keys are symbols?
You can use the Active Support gem to get access to the with_indifferent_access method:
require 'active_support/core_ext/hash/indifferent_access'
> hash = { somekey: 'somevalue' }.with_indifferent_access
=> {"somekey"=>"somevalue"}
> hash[:somekey]
=> "somevalue"
> hash['somekey']
=> "somevalue"
Since your keys are symbols, use symbols as keys.
> hash = { :husband => 'Homer', :wife => 'Marge' }
=> {:husband=>"Homer", :wife=>"Marge"}
> key_variable = :husband
=> :husband
> hash[key_variable]
=> "Homer"
If you use Rails with ActiveSupport, then do use HashWithIndifferentAccess for flexibility in accessing hash with either string or symbol.
family = HashWithIndifferentAccess.new({
'husband' => "Homer",
'wife' => "Marge"
})
somevar = "husband"
puts family[somevar]
#Homer
somevar = :husband
puts family[somevar]
#Homer
The things that you see as a variable-key in the hash are called Symbol is a structure in Ruby. They're primarily used either as hash keys or for referencing method names. They're immutable, and Only one copy of any symbol exists at a given time, so they save memory.
You can convert a string or symbol with .to_sym or a symbol to string with .to_s to illustrate this let me show this example:
strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
symbolArray = [:HTML, :CSS, :JavaScript, :Python, :Ruby]
# Add your code below!
symbols = Array.new
strings.each {|x|
symbols.push(x.to_sym)
}
string = Array.new
symbolArray .each {|x|
string.push(x.to_s)
}
print symbols
print string
the result would be:
[:HTML, :CSS, :JavaScript, :Python, :Ruby]
["HTML", "CSS", "JavaScript", "Python", "Ruby"]
In ruby 9.1 you would see the symbols with the colons (:) in the right instead:
movies = { peter_pan: "magic dust", need_4_speed: "hey bro", back_to_the_future: "hey Doc!" }
I just wanted to make this point a litter more didactic so who ever is reading this can used.
One last thing, this is another way to solve your problem:
movie_ratings = {
:memento => 3,
:primer => 3.5,
:the_matrix => 3,
}
# Add your code below!
movie_ratings.each_key {|k|
puts k.to_s
}
result:
memento
primer
the_matrix

Ruby -- force method_missing *args to be hash?

I want to define a method_missing function for one of my classes, and I want to be able to pass in a hash as the argument list instead of an array. Like this:
MyClass::get_by_id {:id => id}
MyClass::get_by_id {:id => id, :filters => filters}
MyClass::get_by_id {:id => id, :filters => filters, :sort => sort}
As far as I can tell, the args list gets passed in as an array, so keys get dropped and there's no way to tell which arguments is which. Is there a way to force Ruby to treat the argument list in method_missing as a hash?
What issue are you having? This works for me:
class MyClass
def self.method_missing name, args
puts args.class
puts args.inspect
end
end
MyClass.foobar :id => 5, :filter => "bar"
# Hash
# {:id=>5, :filter=>"bar"}
Is this what you are looking for ?
class Foo
def self.method_missing(name,*args)
p args
p name
end
end
Foo.bar(1,2,3)
# >> [1, 2, 3]
# >> :bar
I'm answering my own question based on experimentation that I've done since asking. When using a hash splat on the arg list, you can pass in a hash like this:
MyClass::get_by_id(:id => id, :filters => filters)
And the argument list will look like this:
[
{
:id => id,
:filters => filters
}
]
Ruby places the key/value pairs into a single hash object at location args[0]. If you call the method like this:
MyClass::get_by_id(id, :filters => filters)
Your argument list will be this:
[
id,
{:filters => filters}
]
So basically, key/value pairs are merged together into a single hash and passed in order.

Anonymous method inside hash assignment

Is there anything like this possible in Ruby:
hash = { :foo => 'bar', :bar => lambda{ condition ? return 'value1' : return 'value2'}}
That actual code doesn't work (clearly), and I know I could just do the logic before the hash assignment, but it would be nice to work inside the assignment like this. Is such a thing possible?
You don't need a lambda for that, just this should work:
hash = {
:foo => 'bar',
:bar => condition ? 'value1' : 'value2'
}
Or if you want to use a function result on hash,
hash= {
:foo=> 'foooooo',
:bar=> lambda {
if condition
value1
else
value2
end
}.call
}

Resources