Reputation: 49162
I have an instance variable @limit
which must be greater than 0 and no greater than 20. I currently have code like this:
@limit = (params[:limit] || 10).to_i
@limit = 20 if @limit > 20
@limit = 0 if @limit < 0
This looks ugly. Is there a better way to restrict an integer to a range of values?
Upvotes: 20
Views: 6221
Reputation: 239904
Comparable#clamp
is available in Ruby 2.4.
3.clamp(10, 20)
=> 10
27.clamp(10, 20)
=> 20
15.clamp(10, 20)
=> 15
Upvotes: 33
Reputation: 46379
For convenience, here's a monkey-patch for the "ugly and better" solution that wins the Tin Man's benchmark elsewhere on this page. (It should be a tiny bit faster by returning immediately if the boundary's exceeded.)
class Numeric
def clamp(min, max)
return min if self < min
return max if self > max
self
end
end
Upvotes: 2
Reputation: 160551
Here is a quick benchmark to show which method we should use. Because someone will inevitably say "Use sort_by
because it's faster than sort
", I added it. sort_by
is only faster than sort
when dealing with complex objects. Basic objects, like integers and strings should be handled by sort
.
require 'fruity'
class Numeric
def clamp(min, max)
self < min ? min : self > max ? max : self
end
end
compare do
min_max { [[10, 100].min, 0].max }
sort { [100, 0, 10].sort[1] }
sort_by { [100, 0, 10].sort_by{ |v| v }[1] }
clamp_test { 10.clamp(0, 100) }
original {
limit = 10
limit = 100 if limit > 100
limit = 0 if limit < 0
limit
}
end
With the results being:
Running each test 65536 times. Test will take about 8 seconds.
original is faster than clamp_test by 2x ± 1.0
clamp_test is faster than sort by 6x ± 1.0
sort is faster than min_max by 2x ± 0.1
min_max is faster than sort_by by 2x ± 0.1
Sometimes ugly is better.
Upvotes: 9
Reputation: 14173
If you feel like monkey patching a method, you might do something like this:
class Numeric
def clamp(min, max)
self < min ? min : self > max ? max : self
end
end
# usage
@limit = (params[:limit] || 10).clamp(0, 20)
Upvotes: 6
Reputation: 369054
How about using Enumerable#min
, Enumerable#max
?
For example, to limit the value in range 0..10
:
x = 100
[[10, x].min, 0].max
# => 10
x = -2
[[10, x].min, 0].max
# => 0
x = 5
[[10, x].min, 0].max
# => 5
Alternative, Using Enumerable#sort
:
x = 100
[x, 0, 10].sort[1]
# => 10
x = -2
[x, 0, 10].sort[1]
# => 0
x = 5
[x, 0, 10].sort[1]
# => 5
Upvotes: 19