145

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.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
btw
  • 7,006
  • 9
  • 40
  • 40

10 Answers10

320
if i.between?(1, 10)
  do thing 1 
elsif i.between?(11,20)
  do thing 2 
...
rogerdpack
  • 62,887
  • 36
  • 269
  • 388
93

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

if (1..10) === i
Baldu
  • 5,714
  • 2
  • 30
  • 19
72

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...
Vincent Robert
  • 35,564
  • 14
  • 82
  • 119
42

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
Tim Hoolihan
  • 12,316
  • 3
  • 41
  • 54
12

You could use
if (1..10).cover? i then thing_1 elsif (11..20).cover? i then thing_2

and according to this benchmark in Fast Ruby is faster than include?

10

You can usually get a lot better performance with something like:

if i >= 21
  # do thing 3
elsif i >= 11
  # do thing 2
elsif i >= 1
  # do thing 1
Brad Werth
  • 17,411
  • 10
  • 63
  • 88
6

Not a direct answer to the question, but if you want the opposite to "within":

(2..5).exclude?(7)

true

Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
6

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!

Ivan Olshansky
  • 889
  • 2
  • 17
  • 23
1

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.

m104
  • 1,118
  • 7
  • 6
-2

For Strings:

(["GRACE", "WEEKLY", "DAILY5"]).include?("GRACE")

#=>true

vidur punj
  • 5,019
  • 4
  • 46
  • 65