How can I select a statement only once, instead of repeating the test inside a loop?
Here's what I'm trying to refactor:
items.each_with_index do |item, ndx|
if side == :left
condition = ndx < max
else
condition = ndx >= max
end
result_items << item if condition
end
I would like to be able to move this if / else / end outside the block, since the value side
never changes inside the block. Since the only value-based change side
is the operator, I would like to do something like:
if side == :left
oper = <
else
oper = >=
end
items.each_with_index do |item, ndx|
result_items << item if ndx oper max
end
But of course this won't work because of the syntax.
Is there anyway to keep the statement, or is there a better way to accomplish what I've done here?
source to share
Operators are just methods:
ndx.public_send(:<=, max)
Same as
ndx <= max
So outside of the loop, set oper
to the symbol for the required statement and inside
result_items << item if ndx.public_send(oper, max)
(you'll have to use send
, not public_send
if you're still stuck on 1.8)
source to share
Check "proc" and "lambda". They allow you to reference the letsay function, an anonymous function:
[4] pry(main)> func = proc {|left, right| left < right}
=> #<Proc:0x34c83c0@(pry):4>
[5] pry(main)> func.call(4,5)
=> true
[6] pry(main)> func.call(5,2)
=> false
Note that "proc" expects a "block" to be passed and that block will later be "called" with the given parameters. This is the same syntax as if you were using map
or each
- all the blocks are there. proc
just returns that block to you, not calls it.
So your code might look like this:
if side == :left
oper = proc{|a,b| a<b}
else
oper = proc{|a,b| a>=b}
end
items.each_with_index do |item, ndx|
result_items << item if oper.call(ndx, max)
end
I have not tested this code, it may have some typos.
source to share