Storing a function in the value for a key within a hash

Viewed 486

Right now, my ruby code looks like this:

case step
when 1
   # step_two_function
when 2
   # step_three_function
when 3
   # step_four_function
end

However, I'm wondering if I can store this in a hash to make things a little more simpler?

For example,

h = {
   1 => step_two_function,
   2 => step_three_function,
   3 => step_four_function,
}

And be able to just simply call the associated function with something like h[step]. It seems that, in Ruby, the function is executed at the same time as the hash being created, rather than only when the value of the key is called.

Is doing this possible and, if so, how?

In python, you would just simply do:

>>> def lies(altons_var):
...     print(altons_var)
... 
>>> a[1] = lies
>>> a[1]('print me please')
print me please

But this doesn't work in Ruby because the minute you call the function (whether directly or from a hash), it executes the function.

5 Answers

Use a Proc or Lambda as Hash Values

The functionality you're after is likely to be best represented by a Proc or lambda. These objects are closures that can be passed around and executed later. As a somewhat contrived example, consider the following code:

step = {
  1 => ->{p "foo"},
  2 => ->{p "bar"},
  3 => ->{p "baz"},
}

step.keys.map { |i| step[i].call }
#=> ["foo", "bar", "baz"]

Each value in the hash contains a "stubby lambda" that you can invoke with the Proc#call method. Using step.keys.map simply iterates over all the keys in your hash and calls each one in turn, but you can also call a single lambda from the hash with step[1] or similar.

You can have method() here. For hash key, you can use any [symbol, string]

Ex:

    def foo(num)
          return num % 2
    end
    
    hash = { :func_name => method(:foo) }

hash[:func_name].call(10) #=> 0

You can also achieve it using Proc, Lambda also

You can use the send keyword

send h[step]

since you writing the method name directly in value part of the hash, the call is being made, but If you store the method names as a string and then if you call by send method as shown below, it would work.

def hi
  puts 'hi'
end

def hello
  puts 'hello'
end

h = {
    1 => 'hi',
    2 => 'hello',
}

send h[1]

Storing a function in the value for a key within a hash

Ruby doesn't have functions. What you have there, are methods, not functions.

There are multiple possibilities. You can store Methods in the Hash. In order to get a Method object, you can use the Object#method method:

h = {
  1 => method(:step_two_function),
  2 => method(:step_three_function),
  3 => method(:step_four_function),
}

Methods have a Method#call method, so you can call them like this:

h[2].call

Or, by using the call syntactic sugar:

h[2].()

The second possibility is to use Procs instead of Methods:

h = {
  1 => -> {
    # contents of `step_two_function`
  },
  2 => -> {
    # contents of `step_three_function`
  },
  3 => -> {
    # contents of `step_four_function`
  },
}

Or, if that is not possible, you can call the methods from the Procs:

h = {
   1 => -> { step_two_function },
   2 => -> { step_three_function },
   3 => -> { step_four_function },
}

Procs also have a Proc#call method, so you can call them the same way you can call Methods.

The last option would be to store the names of the methods in the Hash:

h = {
  1 => :step_two_function,
  2 => :step_three_function,
  3 => :step_four_function,
}

And then you can use Object#public_send:

public_send(h[2])

Please, don't use Object#send unless you absolutely have to, i.e. if you absolutely need to circumvent Module#private access restrictions.

I would store the functions as Proc objects:

 h = {
     1 => :step_to_function.to_proc, 
     2 =>  proc { ... } 
     # etc.
 }

and then invoke it as

h[1].call
h[2].call

IMO, this is the most readable way, because we can see clearly what's going on, on the defining side and on the calling side. It also makes it easy to define a function at the place, without giving it a name, as I showed with h[2].

Related