Determining if a variable is within range?

Viewed 86602

I need to write a loop that does something like:

if i (1..10)
  do thing 1
elsif i (11..20)
  do thing 2
elsif i (21..30)
  do thing 3
etc...

But so far have gone down the wrong paths in terms of syntax.

10 Answers

Use the === operator (or its synonym include?)

if (1..10) === i

As @Baldu said, use the === operator or use case/when which internally uses === :

case i
when 1..10
  # do thing 1
when 11..20
  # do thing 2
when 21..30
  # do thing 3
etc...

if you still wanted to use ranges...

def foo(x)
 if (1..10).include?(x)
   puts "1 to 10"
 elsif (11..20).include?(x)
   puts "11 to 20"
 end
end

If you need the fastest way to do it, use the good old comparing.

require 'benchmark'

i = 5
puts Benchmark.measure { 10000000.times {(1..10).include?(i)} }
puts Benchmark.measure { 10000000.times {i.between?(1, 10)}   }
puts Benchmark.measure { 10000000.times {1 <= i && i <= 10}   }

on my system prints:

0.959171   0.000728   0.959899 (  0.960447)
0.919812   0.001003   0.920815 (  0.921089)
0.340307   0.000000   0.340307 (  0.340358)

As you can see, double comparing is almost 3 times faster than #include? or #between? methods!

A more dynamic answer, which can be built in Ruby:

def select_f_from(collection, point) 
  collection.each do |cutoff, f|
    if point <= cutoff
      return f
    end
  end
  return nil
end

def foo(x)
  collection = [ [ 0, nil ],
                 [ 10, lambda { puts "doing thing 1"} ],
                 [ 20, lambda { puts "doing thing 2"} ],
                 [ 30, lambda { puts "doing thing 3"} ],
                 [ 40, nil ] ]

  f = select_f_from(collection, x)
  f.call if f
end

So, in this case, the "ranges" are really just fenced in with nils in order to catch the boundary conditions.

Related