Let's say x
, a
and b
are numbers. I need to limit x
to the bounds of the segment [a, b]
.
In other words, I need a clamp function:
clamp(x) = max( a, min(x, b) )
Can anybody come up with a more readable version of this?
Let's say x
, a
and b
are numbers. I need to limit x
to the bounds of the segment [a, b]
.
In other words, I need a clamp function:
clamp(x) = max( a, min(x, b) )
Can anybody come up with a more readable version of this?
The way you do it is pretty standard. You can define a utility clamp
function:
/**
* Returns a number whose value is limited to the given range.
*
* Example: limit the output of this computation to between 0 and 255
* (x * 255).clamp(0, 255)
*
* @param {Number} min The lower boundary of the output range
* @param {Number} max The upper boundary of the output range
* @returns A number in the range [min, max]
* @type Number
*/
Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};
(Although extending language built-ins is generally frowned upon)
Number.prototype
in this case) is ill-advised for a variety of reasons. See "Bad practice: Extension of native prototypes" at developer.mozilla.org/en-US/docs/Web/JavaScript/… –
Loggerhead const clamp = (num, min, max) => Math.min(Math.max(num, min), max)
. –
Dope Math
and not Number
–
Brazell a less "Math" oriented approach, but should also work, this way, the <
/ >
test is exposed (maybe more understandable than minimaxing) but it really depends on what you mean by "readable"
function clamp(num, min, max) {
return num <= min
? min
: num >= max
? max
: num
}
Math.random()
call, this simple approach is 10 x faster. –
Travis function clamp(num, min = -Infinity, max = Infinity)
will let you clamp using just a min or max param. for example clamp(num, 0) to clamp to a positive number. –
Lehrer clampNumber(n, min, max)
'. the OOP approach of extending Number is messy, leaves you to check for null/undefined before invoking (delegate to function). –
Undress There's a proposal to add an addition to the built-in Math
object to do this:
Math.clamp(x, lower, upper)
But note that as of today, it's a Stage 1 proposal. Until it gets widely supported (which is not guaranteed), you can use a polyfill.
A simple way would be to use
Math.max(min, Math.min(number, max));
and you can obviously define a function that wraps this:
function clamp(number, min, max) {
return Math.max(min, Math.min(number, max));
}
Originally this answer also added the function above to the global Math
object, but that's a relic from a bygone era so it has been removed (thanks @Aurelio for the suggestion)
prototype
very elegant when it comes to actually use the function. –
Bernete Math.max(min, Math.min(number, max))
(the relevant bit) is still valid and worth an upvote, so @Phalarope would you consider editing the answer and avoiding the prototype mutation? I think some upvotes you would get are diverted to this answer –
Dope Math.min(Math.max(MIN, VALUE), MAX)
, and it's easier to remember: min+max
functions, and min value max
arguments. –
Maxinemaxiskirt This does not want to be a "just-use-a-library" answer but just in case you're using Lodash you can use .clamp
:
_.clamp(yourInput, lowerBound, upperBound);
So that:
_.clamp(22, -10, 10); // => 10
Here is its implementation, taken from Lodash source:
/**
* The base implementation of `_.clamp` which doesn't coerce arguments.
*
* @private
* @param {number} number The number to clamp.
* @param {number} [lower] The lower bound.
* @param {number} upper The upper bound.
* @returns {number} Returns the clamped number.
*/
function baseClamp(number, lower, upper) {
if (number === number) {
if (upper !== undefined) {
number = number <= upper ? number : upper;
}
if (lower !== undefined) {
number = number >= lower ? number : lower;
}
}
return number;
}
Also, it's worth noting that Lodash makes single methods available as standalone modules, so in case you need only this method, you can install it without the rest of the library:
npm i --save lodash.clamp
number === number
? Surely it always evaluates to true
? –
Shaneka number === number
is false is NaN
, and that is a case you usually want to handle when writing number logic. Object.is(number, number)
always returns true. –
Nessus isNaN
instead of using ===
lol –
Jenks If you are able to use es6 arrow functions, you could also use a partial application approach:
const clamp = (min, max) => (value) =>
value < min ? min : value > max ? max : value;
clamp(2, 9)(8); // 8
clamp(2, 9)(1); // 2
clamp(2, 9)(10); // 9
or
const clamp2to9 = clamp(2, 9);
clamp2to9(8); // 8
clamp2to9(1); // 2
clamp2to9(10); // 9
If you don’t want to define any function, writing it like Math.min(Math.max(x, a), b)
isn’t that bad.
This expands the ternary option into if/else which minified is equivalent to the ternary option but doesn't sacrifice readability.
const clamp = (value, min, max) => {
if (value < min) return min;
if (value > max) return max;
return value;
}
Minifies to 35b (or 43b if using function
):
const clamp=(c,a,l)=>c<a?a:c>l?l:c;
Also, depending on what perf tooling or browser you use you get various outcomes of whether the Math based implementation or ternary based implementation is faster. In the case of roughly the same performance, I would opt for readability.
In the spirit of arrow sexiness, you could create a micro clamp/constrain/gate/&c. function using rest parameters
var clamp = (...v) => v.sort((a,b) => a-b)[1];
Then just pass in three values
clamp(100,-3,someVar);
That is, again, if by sexy, you mean 'short'
clamp
, perf will take a serious hit if you have complex logic (even if it is "sexy") –
Permission My favorite:
[min,x,max].sort()[1]
[1,5,19].sort()[1]
returns 19. It could be fixed like this: [min,x,max].sort(function(a, b) { return a - b; })[1]
but this isn't sexy anymore. Beside when heavily used it may be a performance issue to create a new array just to compare three numbers. –
Rubiaceous [min, x, max].sort((a,b) => a-b)[1]
–
Nonu © 2022 - 2024 — McMap. All rights reserved.