Collecting keyword arguments in Ruby - ruby

Just trying to understand how to collect arguments in Ruby, I came up with the following snippet, that seems to fail to collect keyword arguments in **kargs in some case:
def foo(i, j= 9, *args, k: 11, **kargs)
puts "args: #{args}; kargs: #{kargs}"
end
foo(a: 7, b: 8)
# output: args: []; kargs: {}
foo(9, a: 7, b: 8)
# output: args: []; kargs: {:a=>7, :b=>8}
foo(9, 10, 13, a: 7, b: 8)
# output: args: [13]; kargs: {:a=>7, :b=>8}
I would like to know why it does not collect kargs in the first call to foo, while in the second call it does.

It's because the first parameter i, is a required parameter (no default value), so, the first value passed to the method (in your first example, this is the hash {a: 7, b: 8}) is stored into it.
Then, since everything else is optional, the remaining values (if any, in this example, there are none) are filled in, as applicable. IE, the second parameter will go to j, unless it is a named parameter, then it goes to kargs (or k). The third parameter, and any remaining--up until the first keyword argument, go into args, and then any keyword args go to kargs
def foo(i, j= 9, *args, k: 11, **kargs)
puts "i: #{i}; args: #{args}; kargs: #{kargs}"
end
foo(a: 7, b: 8)
# i: {:a=>7, :b=>8}; args: []; kargs: {}

Related

Return an argument's name instead of its value - Ruby

I need to print the result of a .max function, but displaying the name of its argument, not its target value (a number) used by the .max method.
def who_is_bigger(a,b,c)
"#{[a,b,c].max} is bigger"
end
Expected result :
expect(who_is_bigger(84, 42, 21)).to eq("a is bigger")
expect(who_is_bigger(42, 84, 21)).to eq("b is bigger")
expect(who_is_bigger(42, 21, 84)).to eq("c is bigger")
As far as I'm aware, there's not an effortless or easy-to-understand way to get the name of an argument from its value. (See the bottom of the answer for one way of doing it like this.)
Instead, here's the way I'd do it, which I think is nice and readable:
def who_is_bigger(a,b,c)
values = {a: a, b: b, c: c}
largest_key, _ = values.max_by { |key, value| value }
"#{largest_key} is bigger"
end
This works as follows:
Build a hash out of the arguments, and store it in values. For example, this hash could be { a: 84, b: 42, c: 21 }.
Find the largest value in that hash using the Enumerable#max_by method. This will store an array of 2 items in largest_pair, such as [:a, 84]. Notice how the key we want is the first item, so we use destructuring to extract the first item of that pair and store it in largest_key.
Construct the string and return it from the method.
If you'd like to do this in such a way where you are accessing and iterating the names and values of the parameters dynamically, you could use this:
def who_is_bigger(a,b,c)
params = method(__method__).parameters
values = params.map { |_, name| [name, binding.local_variable_get(name)] }.to_h
largest_key, _ = values.max_by { |key, value| value }
"#{largest_key} is bigger"
end
In my opinion though, this feels a bit hacky and harder-to-read than the other solution.
Here is another way to do this using Binding and max_by from Ruby:
def who_is_bigger(a, b, c)
biggest = binding.local_variables.max_by do |v|
binding.local_variable_get(v) || -Float::INFINITY
end
"#{biggest} is biggest"
end
who_is_bigger(10, 21, 30)
=> "c is biggest"
who_is_bigger(40, 31, 30)
=> "a is biggest"

Ruby default value for def

i have ruby method
def get(name1="john", age=31)
puts "#{name1} + #{age}"
end
how to allocate only a variable "age"
For example:
get(:age => 3)
=> john + 3
When Ruby encounters
def m(a=1, b=2, c=3)
puts "a=#{a}, b=#{b}, c=#{3}"
end
This is what happens:
m(4,5)
# a=4, b=5, c=3
Ruby has no way of knowing which variable you want assigned to its default so she employs a simple rule: assign arguments to variables left to right until they are used up, then any remaining arguments are assigned to their default values.
If you want a different priority you could change the order of the arguments, say,
def m(b=2, c=3, a=1)
puts "a=#{a}, b=#{b}, c=#{3}"
end
m(4,5)
# a=1, b=4, c=3
This may suffice in some situations but a more flexible solution, which also reads better, is to use named arguments (aka named paramters), as #steenstag has done in his solution. Here that might be as follows.
def m(a: 1, b: 2, c: 3)
d = a + c
puts "a=#{a}, b=#{b}, c=#{c}, d=#{d}"
end
m(:b=>4, :c=>5)
# a=1, b=4, c=5, d=6
m(c: 4)
# a=1, b=2, c=4, d=5
Notice that I've written c=>5 in the first instance and c: 4 in the second. Either form can be used. We cannot, however, write def m(:a=>1, :b=>2, :c=>3).
Since Ruby v2.1 we can also have required named arguments:
def m(a:, b: 2, c: 3)
puts "a=#{a}, b=#{b}, c=#{c}"
end
m(c:5, a: 4)
# a=4, b=2, c=5
m(c: 5)
# ArgumentError (missing keyword: a)
We can also have some named and some unnamed arguments, provided the named arguments are at the end, but its less confusing to simply name all arguments if any are named.
def m(d, a:, b: 2, c: 3)
puts "d=#{d}, a=#{a}, b=#{b}, c=#{c}"
end
m(6, c:5, a: 4)
# d=6, a=4, b=2, c=5
def m(a:, b: 2, c: 3, d)
end
#=> SyntaxError ((irb):165: syntax error, unexpected tIDENTIFIER)
# ... def m(a:, b: 2, c: 3, d)
# ^
You're very close:
def get(name1: "john", age: 31)
puts "#{name1} + #{age}"
end
get(:age => 3) # => john + 3

Why is the result of this array 9 and not the contents of the array?

Why does:
code3_x = []
level = 7
(level + 2).times do |i|
# This is more what I want:
# code3_x[i] << i
# This works to demonstrate:
code3_x << i
end
return:
=> 9
Why not?
=> [0,1,2,3,4,5,6,7,8,9]
I create an array as code3_x & I have a value x (replace the second i with a conditionally chosen number, just reusing i in the example as a place holder) I want to insert x at a particular index in code3_x.
note:
I'm trying to translate the following javascript to ruby
this is just a snip of a larger function
if ... conditions ...{
code3_x[i] =2;
mod_x -= h_pow;
}else...
Update:
here is a link to a gist of the entire javascript function I am trying to re-implement in ruby.
https://gist.github.com/therocketforever/d1dca656f4579bc5baf3
I guess you have a typo in that code. << is not defined on code3_x[i] (which is nil).
The version with code3_x[i] = i returns 9 because the return value of #times is the object you're sending the message to (in this case (level + 2)). code3_x will still be set:
(level + 2).times do |i|
code3_x[i] = i
end # => 9
code3_x # => [0,1,2,3,4,5,6,7,8]
If you need the return value, you could for example use #map with a range
(0..9).map{ |i| i * i } # => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
because you're doing a bit-shift ;) try
code3_x[i] = i

Ruby Methods to Return Individual Letters

I wondering if there is a way to return the first letter of a word. like if you type in word("hey") it will return just the letter h. or if you wanted to you could return the letter e. individually by themselves. I was considering using the break method or scan but I can't seem to make them work.
another method you can look at is chr which returns the first character of a string
>> 'hey'.chr # 'h'
you can also look at http://www.ruby-doc.org/core-1.9.3/String.html#method-i-slice to see how you can combine regexp and indexes to get a part of a string.
UPDATE: If you are on ruby 1.8, it's a bit hackish but
>> 'hey'[0] # 104
>> 'hey'[0..0] # 'h'
>> 'hey'.slice(0,1) # 'h'
Yes:
def first_letter(word)
word[0]
end
Or, if using Ruby 1.8:
def first_letter(word)
word.chars[0]
end
Use the syntax str[index] to get a specific letter of a word (0 is first letter, 1 second, and so on).
This is a naive implementation, but you could use method_missing to create a DSL that'd allow you to query a word for letters at different positions:
def self.method_missing(method, *args)
number_dictionary = {
first: 1,
second: 2,
third: 3,
fourth: 4,
fifth: 5,
sixth: 6,
seventh: 7,
eighth: 8,
ninth: 9,
tenth: 10
}
if method.to_s =~ /(.+)_letter/ && number = number_dictionary[$1.to_sym]
puts args[0][number - 1]
else
super
end
end
first_letter('hey') # => 'h'
second_letter('hey') # => 'e'
third_letter('hey') # => 'y'
Using your example - the word "hey":
h = "hey"
puts h[0]
This should return h.

Ruby Range printing out extra

I'm new to coding so please free to point out any errors in the way I refer to code.
rows = 5
(1..rows).each do |n|
print n, ' '
end
This prints out what I expect it to: 1 2 3 4 5.
But, when I put it into a method:
def test(rows)
(1..rows).each do |n|
print n, ' '
end
end
puts test(5)
I get 1 2 3 4 5 1..5.
Why does the 1..5 show up? And how do I get rid of it?
I need it in the method because I plan to add more code to it.
each on a Range returns the range after the looping is done, and you're probably printing the return value of test too.
Just run test(5) instead of puts test(5) or something.
Ruby always returns the last line of any function.
You are executing puts test(5), and test(5) prints the data you expect, and the extra puts prints out the data returned by test(5) method.
Hope that answers your question.
The final 1..5 is the return value from the script. You get that when you run the code in IRB. When you run that as a standalone Ruby script, it will not show up, so you do not need to worry about it.
A Ruby function will return the last statement, in your case 1..5. To illustrate I'll give it a different return value:
def test(rows)
(1..rows).each {|n| puts "#{ n } "}
return 'mashbash'
end
# Just the function invokation, only the function will print something
test(5) # => "1 2 3 4 5 "
# Same as above, plus printing the return value of test(5)
puts test(5) # => "1 2 3 4 5 mashbash"
You could write your example a little differently to achieve what you like:
def second_test(rows)
# Cast range to an array
array = (1..rows).to_a # [1, 2, 3, 4, 5]
array.join(', ') # "1, 2, 3, 4, 5", and it is the last statement => return value
end
# Print the return value ("1, 2, 3, 4, 5") from the second_test function
p second_test(5)
# => "1, 2, 3, 4, 5"

Resources