Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to choose an operator just one time, instead of repeating the test inside a loop?

Tags:

ruby

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'd like to be able to move that if/else/end outside the block, since the value of side never changes inside the block. Since the only thing changing based on the value of side is the operator, I'd like to be able 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, that won't work because of the syntax.

Is there anyway to store an operator, or a better way to accomplish what I'm after here?

like image 393
croceldon Avatar asked Dec 29 '25 08:12

croceldon


2 Answers

Operators are just methods:

ndx.public_send(:<=, max)

Is the same as

ndx <= max

So outside the loop set oper to the symbol for the desired operator and inside

result_items << item if ndx.public_send(oper, max)

( you'll have to use send rather than public_send if you're still stuck on 1.8)

like image 147
Frederick Cheung Avatar answered Jan 01 '26 20:01

Frederick Cheung


Check out "proc" and "lambda". They enable you to hold a reference to, letssay, a 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 the 'proc' expects a "block" to be passed, and that block will be later "call"ed with given params. This is the same syntax as if you'd use a map or each - that's all blocks there. proc just returns you that block instead of invoking it.

So, your code can look similar to:

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've not checked that code, can have some typos.

like image 32
quetzalcoatl Avatar answered Jan 01 '26 19:01

quetzalcoatl