Reputation: 1068
Lately when coding in Ruby, when I need a variable assigned differently depending on some input to a method, I've been using a Hash like this:
variable = { "option1" => [1,2,3], "option2" => [3,2,1]}[input]
The thing I like most about this approach is the scalability: the input can be one of, say, five different values, which with if
statements would be a bit messy.
But what am I losing when doing this? Larger memory use as the Hash needs to be initialized? Slower? Faster?
For comparison, an equivalent if
statement could be:
variable = Array.new
if input.eql?("option1")
variable = [1,2,3]
else
variable = [3,2,1]
end
(Yes, I could have used ?
notation for two options, but not for more, so I wanted to show the structure in comparison.)
Upvotes: 1
Views: 601
Reputation: 230346
One of reasons I prefer hash approach is that it shifts logic to the data itself. Now if you add more rules, you just have to change data, not the code. And you can load data from external source (config file, network, etc.). In short: flexibility.
If we are talking about performance, then these two approaches are pretty much identical. I doubt that you'll be able to detect any differences (in a real world app).
Upvotes: 3
Reputation: 15954
If you are using the hash, be sure to define it once (as a constant).
Some benchmarking (for runtime):
#!/usr/local/bin/ruby -w
require 'benchmark'
def by_hash1(i)
{ "option1" => [1,2,3], "option2" => [3,2,1] }[i]
end
TheHash = {
"option1" => [1,2,3],
"option2" => [3,2,1],
}
def by_hash2(i)
TheHash[i]
end
def by_case(i)
case i
when 'option1'
[1, 2, 3]
when 'option2'
[3, 2, 1]
end
end
def by_if(i)
if i.equal?('option1')
[1, 2, 3]
else
[3, 2, 1]
end
end
class Foo
def self.option1
[1, 2, 3]
end
def self.option2
[3, 4, 5]
end
end
N = 10_000_000
Inps = %w{ option1 option2 }
Benchmark.bm(10) do | x |
x.report('by hash1') { N.times { by_hash1(Inps.sample) } }
x.report('by hash2') { N.times { by_hash2(Inps.sample) } }
x.report('by case') { N.times { by_case(Inps.sample) } }
x.report('by if') { N.times { by_if(Inps.sample) } }
x.report('meta') { N.times { Foo.send(Inps.sample) } }
end
gives
user system total real
by hash1 11.529000 0.000000 11.529000 ( 11.597000)
by hash2 2.387000 0.000000 2.387000 ( 2.401000)
by case 3.151000 0.000000 3.151000 ( 3.155000)
by if 3.198000 0.000000 3.198000 ( 3.236000)
meta 3.541000 0.000000 3.541000 ( 3.554000)
on my ruby 2.0.0p195 (2013-05-14) [x64-mingw32].
Please note that I think performance is normally of minor importance. Only if you encounter performance problems you should start investigating. Otherwise, things like readability are more important.
Upvotes: 3
Reputation: 17631
I see two more options:
Using case
case input
when 'option1'
[1, 2, 3]
else
# default here or error message
end
Using metaprogramming:
class Foo
def option1
[1, 2, 3]
end
def option2
[3, 4, 5]
end
end
Foo.new.send(input)
A benchmark would help us determine the performance of all the solutions given. However, performance is great but code smell, readability and reusability is something your need to be aware of too. Using a Hash is more flexible than a if else
statement but you may found yourself reaching the limit of the hash
implementation at some point.
case
are more readable but can be quite ugly when having lots of conditions...
Metaprogramming is great and gives you lots of flexibility but might be overkill in your case.
Upvotes: 6