세그먼트에 숫자를 제한하는 가장 우아한 방법은 무엇입니까?


127

하자의 말 x, a그리고 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

5
아니, 나는 그것을 사이트에서 얻었다. 의 중첩 / 실행 순서 Math.minMath.max길이의 파라미터뿐만 무관 Math.min하다 max과의 매개 변수 Math.max이다 min.
Otto Allmendinger

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
이 관용구에서 <와>를 <=와> =로 바꾸는 것이 더 좋으므로, 비교가 덜 가능합니다. 그 수정으로, 이것은 내가 선호하는 대답입니다. 최대 최대 값은 혼란스럽고 오류가 발생하기 쉽습니다.
Don Hatch

이것이 가장 좋은 대답입니다. 훨씬 더 빠르고, 더 간단하고, 더 읽기 쉽습니다.
tristan

5
더 빠르지 않습니다. Math.min / max 솔루션이 가장 빠름 jsperf.com/math-clamp/9
Manuel Di Iorio

1
@ManuelDiIorio 응? 현재 Chrome 버전에서는 간단한 삼항이 Math.min / max보다 두 배 빠릅니다. 훨씬 비싼 Math.random()통화 에서 오버 헤드를 제거하면 이 간단한 접근 방식이 10 배 더 빠릅니다 .
mindplay.dk 8


48

ECMAScript 2017 업데이트 :

Math.clamp(x, lower, upper)

그러나 현재로서는 1 단계 제안 입니다. 광범위하게 지원 될 때까지 polyfill을 사용할 수 있습니다 .


4
이 제안 및 기타 제안을 추적하려면 다음을 참조하십시오 : github.com/tc39/proposals
gfullam

5
나는 TC39에서이 제안을 찾을 수 없습니다 / 제안 REPO
콜린 D

찾는 사람의 경우 제안서가 1 단계 제안서 에 "Math Extensions"로 나열됩니다 . 실제 제안은 다음과 같습니다. 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

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];

그런 다음 세 가지 값을 전달하십시오.

clamp(100,-3,someVar);

다시 말하지만, 섹시하다면 '짧다'는 뜻입니다


당신은 배열을 사용하지 말아야하고, 대한 정렬하는 것은 clamp당신이 (이것은 "섹시한"인 경우에도) 복잡한 논리가있는 경우, 반환 한 심각한 타격을 것입니다
앤드류 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 기반 구현 또는 3 진 기반 구현 중 어느 것이 더 빠른지에 대한 다양한 결과를 얻을 수 있습니다. 대략 동일한 성능의 경우 가독성을 선택합니다.


-5

내가 좋아하는 것:

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

12
작동하지 않기 때문에 다운 보팅 [1,5,19].sort()[1]이것은 다음과 같이 수정 될 수 있습니다. [min,x,max].sort(function(a, b) { return a - b; })[1]그러나 이것은 더 이상 섹시하지 않습니다. 많이 사용하는 것 외에도 세 개의 숫자를 비교하기 위해 새 배열을 만드는 것이 성능 문제 일 수 있습니다.
kayahr

5
어쨌든이 +1을주는 것은 섹시하기 때문에 중요합니다.
Don Hatch

3
IMHO 이것은 다른 옵션, 특히 화살표 기능을 사용하여 훨씬 더 읽기 [min, x, max].sort((a,b) => a-b)[1]
쉽습니다

4
이것이 항상 작동하지 않는 이유는 정렬 방법이 숫자 값을 올바르게 비교하지 않기 때문입니다. (그것은 문자열처럼 취급합니다)
John Leuenhagen

효율적이고 분명한 방식으로 작업을 수행하는 적절한 이름의 함수보다 더 섹시한 것은 없다고 생각합니다. 그러나 그것은 맛의 문제 일 수 있습니다.
BallpointBen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.