Búsqueda de sitios web

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

Artículos relacionados