swiftclamp

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


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.


Solution

  • 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)
        }
    }
    
    #if swift(<5.1)
    extension Strideable where Stride: SignedInteger {
        func clamped(to limits: CountableClosedRange<Self>) -> Self {
            return min(max(self, limits.lowerBound), limits.upperBound)
        }
    }
    #endif
    

    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