Standard way to "clamp" a number between two values in Swift

George WS picture George WS · Mar 20, 2016 · Viewed 27.1k times · Source

Given:

let a = 4.2
let b = -1.3
let c = 6.4

I want to know the simplest, Swiftiest way to clamp these values to a given range, say 0...5, such that:

a -> 4.2
b -> 0
c -> 5

I know I can do the following:

let clamped = min(max(a, 0), 5)

Or something like:

let clamped = (a < 0) ? 0 : ((a > 5) ? 5 : a)

But I was wondering if there were any other ways to do this in Swift—in particular, I want to know (and document on SO, since there doesn't appear to be a question about clamping numbers in Swift) whether there is anything in the Swift standard library intended specifically for this purpose.

There may not be, and if so, that's also an answer I'll happily accept.

Answer

Ondrej Stocek picture Ondrej Stocek · Nov 29, 2016

Swift 4/5

Extension of Comparable/Strideable similar to ClosedRange.clamped(to:_) -> ClosedRange from standard Swift library.

extension Comparable {
    func clamped(to limits: ClosedRange<Self>) -> Self {
        return min(max(self, limits.lowerBound), limits.upperBound)
    }
}

// Swift < 5.1
extension Strideable where Stride: SignedInteger {
    func clamped(to limits: CountableClosedRange<Self>) -> Self {
        return min(max(self, limits.lowerBound), limits.upperBound)
    }
}

Usage:

15.clamped(to: 0...10) // returns 10
3.0.clamped(to: 0.0...10.0) // returns 3.0
"a".clamped(to: "g"..."y") // returns "g"

// this also works (thanks to Strideable extension)
let range: CountableClosedRange<Int> = 0...10
15.clamped(to: range) // returns 10