172

假设x和是a数字b。我需要限制x在段的范围内[a, b]

换句话说,我需要一个钳位功能

clamp(x) = max( a, min(x, b) )

任何人都可以想出一个更具可读性的版本吗?

4

10 回答 10

239

你这样做的方式很标准。您可以定义一个效用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);
};

(尽管扩展语言内置插件通常不被接受)

于 2012-07-10T09:02:07.130 回答
85

一种较少面向“数学”的方法,但也应该有效,这样,</>测试就暴露出来了(可能比最小化更容易理解),但这真的取决于你所说的“可读”是什么意思

function clamp(num, min, max) {
  return num <= min 
    ? min 
    : num >= max 
      ? max 
      : num
}
于 2012-07-10T09:10:27.293 回答
68

有一个建议添加到内置Math对象中来做到这一点:

Math.clamp(x, lower, upper)

但请注意,截至今天,它是第 1 阶段提案。在它得到广泛支持(不能保证)之前,您可以使用polyfill

于 2016-09-13T19:50:41.627 回答
31

一个简单的方法是使用

Math.max(min, Math.min(number, max));

你显然可以定义一个包装这个的函数:

function clamp(number, min, max) {
  return Math.max(min, Math.min(number, max));
}

最初这个答案还将上述函数添加到全局Math对象中,但这是过去时代的遗物,因此已被删除(感谢@Aurelio 的建议)

于 2012-07-10T09:03:43.493 回答
15

这不想成为“仅使用库”的答案,但以防万一您使用 Lodash,您可以使用.clamp

_.clamp(yourInput, lowerBound, upperBound);

以便:

_.clamp(22, -10, 10); // => 10

这是它的实现,取自 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;
}

此外,值得注意的是 Lodash 将单个方法作为独立模块提供,因此如果您只需要此方法,您可以在没有库的其余部分的情况下安装它:

npm i --save lodash.clamp
于 2016-11-03T17:36:10.287 回答
9

如果您能够使用 es6 箭头函数,您还可以使用部分应用方法:

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
于 2017-11-02T20:46:15.817 回答
7

如果您不想定义任何函数,那么编写它Math.min(Math.max(x, a), b)并没有那么糟糕。

于 2018-02-15T17:44:32.357 回答
3

这将三元选项扩展为 if/else,它缩小相当于三元选项,但不会牺牲可读性。

const clamp = (value, min, max) => {
  if (value < min) return min;
  if (value > max) return max;
  return value;
}

缩小到 35b(或 43b,如果使用function):

const clamp=(c,a,l)=>c<a?a:c>l?l:c;

此外,根据您使用的性能工具或浏览器,您会得到基于数学的实现还是基于三元的实现更快的各种结果。在性能大致相同的情况下,我会选择可读性。

于 2020-07-21T04:54:23.227 回答
1

本着箭头性感的精神,您可以创建一个微型夹具/约束/门/&c。使用剩余参数的函数

var clamp = (...v) => v.sort((a,b) => a-b)[1];

然后只需传入三个值

clamp(100,-3,someVar);

也就是说,再次,如果性感,你的意思是“短”

于 2017-04-26T18:15:19.037 回答
-7

我的最爱:

[min,x,max].sort()[1]
于 2016-03-04T19:15:06.447 回答