Search code examples
swiftintegerrange

How to interpolate from number in one range to a corresponding value in another range?


I currently receive a Int that could be anywhere between 0 and 180,000. I need to change it to fit between 0 and 3000. I know in other languages you can use something like

Map(min1,max1,min2,max2,input)

I can't seem to find something like that inside of swift. This is what I currently have, but it always returns 0.

var newY = [(Int)(input)]
newY = newY.map {_ in 0 * 3000}
print(newY[0])

I think I am using the wrong function. I have never done any mapping in Swift before.


Solution

  • The map function on collections is going to do something very different. It applies a mapping function to each element of a collection and returns a new collection based on the results.

    What you're looking for would be:

    func map(minRange:Int, maxRange:Int, minDomain:Int, maxDomain:Int, value:Int) -> Int {
        return minDomain + (maxDomain - minDomain) * (value - minRange) / (maxRange - minRange)
    }
    
    print(map(minRange: 0, maxRange: 1800000, minDomain: 0, maxDomain: 3000, value: 200000))
    

    With only a little more work you can make it generic over all integer types:

    func map<T:IntegerArithmetic>(minRange:T, maxRange:T, minDomain:T, maxDomain:T, value:T) -> T {
        return minDomain + (maxDomain - minDomain) * (value - minRange) / (maxRange - minRange)
    }
    

    Another option would be to take advantage of the Swift Range type to make calling more succinct:

    func map<T:IntegerArithmetic>(range:Range<T>, domain:Range<T>, value:T) -> T {
        return domain.lowerBound + (domain.upperBound - domain.lowerBound) * (value - range.lowerBound) / (range.upperBound - range.lowerBound)
    }
    
    map(range:0..<3000, domain:0..<180000, value: 1500)