将数字封顶为细分的最优雅的方法是什么?


Answers:


197

您的操作方式非常标准。您可以定义一个实用程序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);
};

(尽管通常不支持扩展语言内置功能)


很抱歉,您复制/粘贴了我的错字(最大和最小的
错位

5
不,我是从网站上获得的。的嵌套/执行顺序Math.minMath.max是无关紧要的,只要的参数Math.minmax和的参数Math.maxmin
奥托·阿曼丁格

23
Number.prototype出于多种原因,不建议扩展本机原型(在这种情况下)。请参阅developer.mozilla.org/en-US/docs/Web/JavaScript/…上的
broofa,

68

一种不太以“数学”为导向的方法,但也应该起作用,这样,测试就暴露了(可能比minimaxing更容易理解),但这实际上取决于您对“可读”的理解。

function clamp(num, min, max) {
  return num <= min ? min : num >= max ? max : num;
}

4
在这个习惯用法中,最好用<=和> =替换<和>,这样就可以减少比较次数。进行此更正后,这绝对是我的首选答案:极大的分钟数令人困惑并且容易出错。
唐·哈奇

这是最好的答案。更快,更简单,更易读。
tristan

5
不快。Math.min / max解决方案是最快的 jsperf.com/math-clamp/9
Manuel Di Iorio

1
@ManuelDiIorio是吗?在当前版本的Chrome中,简单的三元运算速度是Math.min / max的两倍-如果您从更昂贵的Math.random()调用中消除开销,则这种简单的方法会快10倍
mindplay.dk


48

ECMAScript 2017更新:

Math.clamp(x, lower, upper)

但是请注意,到目前为止,这是第1阶段的提案。在得到广泛支持之前,您可以使用polyfill


4
如果您想跟踪该建议和其他建议,请参见:github.com/tc39/proposals
gfullam

5
我没有找到在TC39这个建议/提议回购
科林d

对于那些寻求帮助的人,该建议在“ 阶段1”建议下列为“数学扩展”。实际的建议在这里:github.com/rwaldron/proposal-math-extensions
WickyNilliams

14
Math.clip = function(number, min, max) {
  return Math.max(min, Math.min(number, max));
}

2
尽管我考虑扩展 prototype实际使用该功能时非常优雅的。
MaxArt 2012年

11

这并不想成为一个“随便使用一个库”的答案,但是如果万一您正在使用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

6

如果您能够使用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

每次您想钳位一个数字时创建函数的效率都非常低
George

1
可能取决于您的用例。这只是创建具有设定范围的夹具的工厂功能。您可以创建一次,然后在整个应用中重复使用。不必多次调用。也不一定适用于不需要锁定设定范围的所有用例。
bingles

@George如果不需要保留范围,只需删除内部箭头功能并将value参数移至外部箭头功能
Alisson Nunes

6

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


0

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

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

然后只需传入三个值

clamp(100,-3,someVar);

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


您不应该对数组使用数组和排序,clamp如果您具有复杂的逻辑(即使它是“性感”的话),perf也会受到严重打击
Andrew McOlash

0

这将三元选项扩展为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;

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


-5

我的最爱:

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

12
拒绝投票,因为它不起作用。[1,5,19].sort()[1]返回19.可以像这样修复:[min,x,max].sort(function(a, b) { return a - b; })[1]但这不再性感了。除了经常使用之外,创建一个仅用于比较三个数字的数组可能是性能问题。
卡亚尔

5
无论如何都要给+1,因为性感才是最重要的。
唐·哈奇

3
恕我直言,这比任何其他选项(尤其是带有箭头功能)的可读性都高得多:[min, x, max].sort((a,b) => a-b)[1]
zaius16年

4
之所以不能始终如此,是因为sort方法无法正确比较数值。(它像对待字符串一样对待它们)
John Leuenhagen

我认为没有什么比恰当地命名的函数更有效,更明显地正确地完成工作了。但这可能是一个品味问题。
BallpointBen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.