4

Is there any way to set a hash with values such as <, >, %, +, etc? I want to create a method that accepts an array of ints, and a hash with parameters.

In the method below array is the array to be filtered, and hash is the parameters. The idea is that any number less than min or more than max is removed.

def range_filter(array, hash) 
  checker={min=> <, ,max => >} # this is NOT  working code, this the line I am curious about
  checker.each_key {|key| array.delete_if {|x| x checker[key] args[key] }
  array.each{|num| puts num}
end

The desired results would be

array=[1, 25, 15, 7, 50]
filter={min=> 10, max=> 30} 
range_filter(array, filter)
# => 25
# => 15
user513951
  • 12,445
  • 7
  • 65
  • 82
Btuman
  • 881
  • 2
  • 9
  • 37
  • Possible duplicate of [Can I dynamically call a math operator in Ruby?](http://stackoverflow.com/questions/13060239/can-i-dynamically-call-a-math-operator-in-ruby) – user513951 Jan 12 '16 at 12:35

4 Answers4

7

In ruby, even math is a method invocation. And math symbols can be stored as ruby symbols. These lines are identical:

1 + 2         # 3
1.+(2)        # 3
1.send(:+, 2) # 3

So armed with that, storing it as a hash is simple:

op = { :method => :> }
puts 1.send(op[:method], 2) # false
puts 3.send(op[:method], 2) # true
Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • `!=` is a special syntax case. It actually executes the `==` method, and then flips the resulting boolean. So you can't run `!=` as a method directly sadly. You have to do `!1.send(:==, 2)`. You may have to detect that symbol and special case it if you want to support this. – Alex Wayne Dec 08 '11 at 21:40
  • Sadly, I'm on 1.8.7 still. But I'm glad to hear they fixed that inconsistency then :) – Alex Wayne Dec 08 '11 at 23:32
  • In Ruby 1.9, `!` is also a method, so it would be more like `1.send(:==, 2).send(:!)`. – Jörg W Mittag Dec 09 '11 at 03:27
5

sure, store them as strings (or symbols) and use object.send(function_name, argument)

> operators = ["<", ">", "%", "+"]
=> ["<", ">", "%", "+"] 
> operators.each{|op| puts ["   10 #{op} 3: ", 10.send(op,3)].join}
   10 < 3: false
   10 > 3: true
   10 % 3: 1
   10 + 3: 13
klochner
  • 8,077
  • 1
  • 33
  • 45
1

This should work just like expected:

def range_filter(array, args)
  checker = { :min=> :<, :max => :> }
  checker.each_key { |key| array.delete_if {|x| x.send checker[key], args[key] } }
  array.each { |num| puts num }
end

Just use Symbols instead of the plain operators. The operators are special methods of number objects so you can just use send and their Symbol equivalent to call them dynamically.

Koraktor
  • 41,357
  • 10
  • 69
  • 99
0

Guess using symbols doesn't add readability in this case. Try this:

checkers = 
  [ lambda{ |x| x > 10 },
    lambda{ |x| x < 30 } ]

[1, 25, 15, 7, 50].select{ |x| checkers.all?{ |c| c[x] } } 
#=> [25, 15]

Update

Compare with (it works too, but what if you wanted lambda{ |x| x % 3 == 1 } ?)

checkers = 
  { :> => 10,
    :< => 30 }

[1, 25, 15, 7, 50].select{ |x| checkers.all?{ |k, v| x.send(k, v) } } 
#=> [25, 15]
Victor Moroz
  • 9,167
  • 1
  • 19
  • 23