user1207289
user1207289

Reputation: 3253

How do I dynamically call a method that takes an array as parameters?

I've a method like:

def method (a=[],b=[])
 ...
end

I am calling this method with variables like this:

method(h[0].values_at("value"), h[1].values_at("value")) 

where h[0] and h[1] are hashes. It works fine.

I dont know if h[1] is going to be there in the next run, so it's giving me error if hash h[1] is not there.

How can I modify it so it makes the call dynamically depending on whether h[0], h[1] are there or not, and call the method with the correct number of parameters.

Upvotes: 1

Views: 351

Answers (4)

Harish Shetty
Harish Shetty

Reputation: 64363

You can change the function signature to accept variable arguments.

def foo(*args)
  options = args.extract_options!
  p options
  p args 
end
  • Invocation without parameters

    foo()
    {}
    []
    
  • Invocation with 2 parameters

    foo(1, 2)
    {}
    [1, 2]
    
  • Invocation with 3 parameters

    foo(1, 2, 3)
    {}
    [1, 2, 3]
    
  • Invocation with 3 parameters and an option hash

    foo(1, 2, 3, :debug => true)
    {:debug=>true}
    [1, 2, 3]
    

Upvotes: 0

knut
knut

Reputation: 27845

Hope I understood your problem right:

method(h[0].values_at("value"), 
        h[1] ? h[1].values_at("value") : []
      ) 

Your problem: if h[1]does not exist, h[1].values_at will raise an exception. So you have to test first, if the value is available. In the code snipllet above I used a ternary operator.

An extended version would be:

par2 = []
par2 = h[1].values_at("value") if h[1]

method(h[0].values_at("value"), 
        par2
      ) 

With my solution you don't need the default values in the method definition.


In your comment you extended your question.

With four parameters you could use it like this:

def method(p1,p2,p3,p4)
  #...
end

method(
    h[0] ? h[0].values_at("value") : [],
    h[1] ? h[1].values_at("value") : [],
    h[2] ? h[2].values_at("value") : [],
    h[3] ? h[3].values_at("value") : [],
  ) 

But I would recommend a more generic version:

def method(*pars)
  #pars is an Array with all values (including empty arrays.
  #The next check make only sense, if you need exactly 4 values.
  raise "Expected 4 values, get #{pars.size}"  unless pars.size == 4
end

method( *(h.map{|i|i.values_at("x")})

And another - probably not so good - idea:

Extend nil (the result of h[1] if h has not this element) to return [] for values_at:

class NilClass  
  def values_at(x)
    []
  end
end

Upvotes: 1

ka8725
ka8725

Reputation: 2918

If you use Ruby On Rails you are able to execute try method:

method(h[0].values_at("value"), h[1].try(:values_at, 'value') || [])

Examples for method try:

nil.try('[]', :x) # => nil
{:x => 't'}.try('[]', :x) # => 't'

Upvotes: 0

AShelly
AShelly

Reputation: 35520

The simplest way to do exactly what you ask would be:

if h[1]
  method(h[0].values_at("x"), h[1].values_at("x"))
else
  method(h[0].values_at("x"))
end

Another idea is to put a default hash in the case where h[1] is nil

method(h[0].values_at("x"), (h[1]||{}).values_at("x") )

If you are sure h never has more than 2 items, you can do:

method *(h.map{|i|i.values_at("x")})

Upvotes: 1

Related Questions