セグメントに数値をキャップする最もエレガントな方法は何ですか?


127

としましょうxaそしてb数字です。xセグメントの境界に上限を設定する必要があります[a, b]

書けますがMath.max(a, Math.min(x, b))、とても読みやすいとは思いません。誰かがこれをもっと読みやすい方法で書く賢い方法を持っていますか?

回答:


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);
};

(ただし、組み込み言語の拡張は一般的に推奨されません)


あなたが私のタイプミスをコピー/貼り付けたOOps(最大と最小を入れ替え)
Samuel Rossille '10 / 07/12

5
いいえ、サイトから取得しました。ネスト/実行順序Math.minとはMath.max長いのパラメータと同様に無関係でMath.minあるmaxとのパラメータMath.maxですmin
Otto Allmendinger 2012

23
ネイティブプロトタイプ(Number.prototypeこの場合)の拡張は、さまざまな理由で不適切です。developer.mozilla.org/en-US/docs/Web/JavaScript/…にある
broofa

68

あまり「数学」指向のアプローチではありませんが、この方法でも機能するはずです。この方法では、</>テストが公開されます(ミニマックスよりも理解しやすいかもしれません)が、実際には「読み取り可能」の意味に依存します

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

4
このイディオムで<と>を<=と> =に置き換えると、比較が1つ少なくなる可能性があります。その修正により、これは間違いなく私の好ましい答えです。最大数の分は混乱を招き、エラーが発生しやすくなります。
ドンハッチ

これが最良の答えです。はるかに速く、はるかにシンプルで、はるかに読みやすくなっています。
トリスタン

5
速くはありません。Math.min / maxソリューションが最速 jsperf.com/math-clamp/9
Manuel Di

1
@manuelDiIorioハァッ?Chromeの現在のバージョンでは、単純な3値はMath.min / maxの2倍の速さです。はるかに高価なMath.random()呼び出しからオーバーヘッドを取り除くと、この単純なアプローチは10倍速くなります。
mindplay.dk 2017年


48

ECMAScript 2017の更新:

Math.clamp(x, lower, upper)

ただし、現在のところ、これはステージ1の提案です。広くサポートされるまでは、ポリフィルを使用できます。


4
この提案などを追跡する場合は、github.com
tc39

5
tc39 / proposalsリポジトリでこの提案が見つかりませんでした
コリン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
おそらくあなたのユースケースに依存します。これは、設定された範囲でクランプを作成するための単なるファクトリ関数です。一度作成すれば、アプリ全体で再利用できます。複数回呼び出す必要はありません。また、設定された範囲でロックする必要がないすべてのユースケースのツールとは限りません。
バングル

@George範囲を維持する必要がない場合は、内側の矢印関数を削除して、値パラメーターを外側の矢印関数に移動します
Alisson Nunes

6

関数を定義したくない場合Math.min(Math.max(x, a), b)は、そのように書くのは悪くありません。


0

矢印のセクシーさの精神で、マイクロクランプ/拘束/ゲート/&cを作成できます。レストパラメータを使用する関数

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

次に、3つの値を渡します

clamp(100,-3,someVar);

つまり、セクシーな場合は、「短い」という意味です


clamp複雑なロジックがある場合(たとえ「セクシー」であっても)、配列とソートを使用しないでください
。perfは深刻な影響を与えます

0

これにより、3項オプションが、最小化されたif / elseに展開され、3項オプションと同等になりますが、読みやすさは犠牲になりません。

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;

また、使用するパフォーマンスツールまたはブラウザーに応じて、数学ベースの実装と3値ベースの実装のどちらが高速であるかについて、さまざまな結果が得られます。ほぼ同じパフォーマンスの場合、読みやすさを選びます。


-5

お気に入り:

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

12
機能しないため反対投票。[1,5,19].sort()[1]これは19を返します。次のように修正できます。[min,x,max].sort(function(a, b) { return a - b; })[1]しかし、これはもうセクシーではありません。頻繁に使用する場合を除いて、3つの数値を比較するためだけに新しい配列を作成すると、パフォーマンスの問題になる可能性があります。
カヤール

5
とにかくこれを+1すること。
ドンハッチ

3
私見これは、特に矢印の機能と、はるかに読みやすい他のオプションのいずれかより:[min, x, max].sort((a,b) => a-b)[1]
zaius

4
これが常に機能しないのは、sortメソッドが数値を適切に比較しないためです。(文字列のように扱います)
John Leuenhagen、

私は、効率よく、明らかに正しい方法で仕事をする適切に名前が付けられた関数よりセクシーなものは何もないと思います。しかし、それは好みの問題かもしれません。
BallpointBen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.