Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to map hash items to method parameters?

I have a method with a lengthy list of optional arguments, such as:

def foo(foo = nil, bar = nil, baz = nil, qux = nil)
    # no-op
end

I thought that calling the method and passing a split hash as a parameter would map the hash items to parameters by matching the key with the method parameter:

params = { bar: 'bar', foo: 'foo' }
foo(*params)

Unfortunately, when I examine the local variables after calling the method with a split hash, I get exactly what I'd expect if I passed in a split array, but it's not what I was hoping for:

foo == [:bar, 'bar'] # hoped: foo == 'foo'
bar == [:foo, 'foo'] # hoped: bar == 'bar'

What am I lacking here?

like image 901
Filip Dupanović Avatar asked Dec 26 '22 19:12

Filip Dupanović


1 Answers

(this answer refers to the version of Ruby that was current when the question was asked. See edit for the situation today.)

Ruby does not support passing arguments by name. The splat operator (*) expands an arbitrary enumerable by calling to_ary on it and splices the result into the argument list. In your case, the enumerable you pass in is a hash, which gets transformed into an array of key-value pairs:

[2] pry(main)> params.to_a
=> [[:bar, "bar"], [:foo, "foo"]]

So the first two arguments of the function will be the values [:bar, "bar"] and [:foo, "foo"] (regardless of their parameter name!).

If you want to have something similar to keyword arguments in Ruby, you can make use of the fact that you don't need the braces when passing a hash to a function as the last argument:

def foo(opts = {})
    bar = opts[:bar] || <default>
    foo = opts[:foo] || <default>
    # or with a lot of parameters:
    opts = { :bar => <default>, :foo => <default>, ... }.merge(opts)
end

foo(foo: 3)  # equivalent to foo({ foo: 3 })

EDIT:

As of version 2.0, Ruby now supports named arguments with a dedicated syntax. Thanks to user jgoyon for pointing that out.

like image 118
Niklas B. Avatar answered Jan 16 '23 04:01

Niklas B.