Is there a method to limit/clamp a number?
Asked Answered
M

5

32

I wrote the following code, which keeps x within the range (a..b). In pseudo code:

(if x < a, x = a; if x > b, x = b)

In Ruby it would be something like:

x = [a, [x, b].min].max

As it is quite basic and useful function, I was wondering if there is a native method to do that in ruby.

As of Ruby 2.3.3 there is apparently no method like this, what would be the shortest/more readable way to do it?

I found:

x = [a, x, b].sort[1]

so far, but I'm not sure if it is more readable.

Magbie answered 18/8, 2012 at 17:37 Comment(7)
Just a matter of interest, from for exactly native function?Milquetoast
You've answered your own question there - nice use of the sort function. I don't think you'll get much more readable than that.Walcoff
you problably mean: x2 = [a, [x, b].min].max. It looks ok to me, use basic generic methods to build the rest.Leacock
Interesting use of sort.. makes the whole thing independant of the order..Engler
@tokland: you are right. I edited itMagbie
I love your cryptic version. You should put it in as an answer so I can up vote it.Incoming
@Incoming Thanks. I found it somewhere (in the R communauty if I remember well). A create an answerMagbie
B
66

Ruby 2.4.0 introduces Comparable#clamp:

523.clamp(0, 100)        #=> 100
Bickford answered 18/8, 2012 at 23:13 Comment(3)
The best part: the reason ruby has no clamp method is they can't stop arguing about what to name the function.Supposition
This has finally been added now.Dabber
Haha, I implemented Comparable#clamp. Took them long enough to merge my patch... ;-)Chatterbox
M
37

My own answer : NO

However

x = [a, x, b].sort[1]

Is a solution.

Magbie answered 27/2, 2014 at 14:39 Comment(3)
Not gonna lie, that's sexy.Decimeter
I think this might be the single prettiest line of code I've ever seen.Clayborne
The cool thing is that the order in which you put the min, max and x does not even matter.Schlemiel
S
12

I did this:

class Numeric
  def clamp min, max
    [[self, max].min, min].max
  end
end

So whenever I want to clamp anything, I can just call:

x.clamp(min, max)

Which I find pretty readable.

Stalkinghorse answered 7/2, 2014 at 20:10 Comment(0)
D
0

Here is my solution which borrows heavily from the actual implementation:

unless Comparable.method_defined? :clamp
  module Comparable
    def clamp min, max
      if max-min<0
        raise ArgumentError, 'min argument must be smaller than max argument'
      end
      self>max ? max : self<min ? min : self
    end
  end
end
Devindevina answered 18/11, 2017 at 3:8 Comment(0)
B
-1

The most appealing solution for now in my opinion is the sort option:

[min,x,max].sort[1] 

When you don't mind monkey patching existing core classes. I think the range class is a good candidate for a clamp method

class Range
  def clamp(v)
    [min,v,max].sort[1]
  end
end

(min..max).clamp(v)

Or the plain array object. I don't like this, because the clamp function only is correct for 3 element arrays

class Array
    def clamp
      sort[1]
    end
end

You can call it like this:

[a,x,b].clamp
Blackburn answered 26/7, 2016 at 14:6 Comment(1)
Calling clamp over an array or a range seems to be really deceiving. That is not a result someone would think about. Monkey patching numeric or comparable looks like something easier to understand IMHO.Salvadorsalvadore

© 2022 - 2024 — McMap. All rights reserved.