javascript - ¿Cuál es la manera más elegante de limitar un número a un segmento?
Digamos que x, a y b son números. Necesito limitar x a los límites del segmento [a, b].
En otras palabras, necesito una función clamp:
clamp(x) = max( a, min(x, b) )
¿Alguien puede crear una versión más legible de esto?
Respuesta #1
La forma en que lo haces es bastante estándar. Puedes definir una función de utilidad clamp:
/**
* 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);
};
(Aunque generalmente se desaprueba extender las funciones integradas del lenguaje)
Respuesta #2
un enfoque menos orientado a las "Matemáticas", pero también debería funcionar, de esta manera, la prueba </> queda expuesta (quizás más comprensible que el minimax), pero realmente depende de lo que quieras decir con "legible"
function clamp(num, min, max) {
return num <= min
? min
: num >= max
? max
: num
}
Respuesta #3
Hay una propuesta para agregar una adición al objeto integrado Math para hacer esto:
Math.clamp(x, lower, upper)
Pero ten en cuenta que a día de hoy, es una propuesta de Etapa 1. Hasta que obtenga un apoyo amplio (lo cual no está garantizado), puedes usar un polyfill.
Respuesta #4
Una manera simple sería usar
Math.max(min, Math.min(number, max));
y obviamente puedes definir una función que envuelva esto:
function clamp(number, min, max) {
return Math.max(min, Math.min(number, max));
}
Originalmente, esta respuesta también añadía la función anterior al objeto global Math, pero eso es un vestigio de una era pasada, por lo que ha sido eliminado (gracias @Aurelio por la sugerencia)
Respuesta #5
Esto no quiere ser una respuesta de "simplemente-usa-una-librería", pero por si acaso estás usando Lodash, puedes usar .clamp:
_.clamp(yourInput, lowerBound, upperBound);
Así que:
_.clamp(22, -10, 10); // => 10
Aquí está su implementación, tomada del código fuente de Lodash:
/**
* 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;
}
Además, vale la pena señalar que Lodash pone a disposición los métodos individuales como módulos independientes, por lo que en caso de que solo necesites este método, puedes instalarlo sin el resto de la biblioteca:
npm i --save lodash.clamp
Respuesta #6
Si puedes usar funciones flecha de ES6, también podrías usar un enfoque de aplicación parcial:
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
