I'm trying to write an algorithm that returns the closest value within a list of values. So in a List(4.0, 6.0, 2.0) the closest value to 7.0 is 6.0
Below is the code I'm using but it's not correct as the closest value being returned is 4 :
How can fix below code or is there a Scala utility method I can use to solve this ?
val num = 7.0 //> num : Double = 7.0
val listNums = List[Double](4,6,2) //> listNums : List[Double] = List(4.0, 6.0, 2.0)
def getClosest(num : Double , listNums : List[Double]) = {
var min = java.lang.Double.MAX_VALUE
var closest = num
for(x <- listNums ){
val diff = x - num
if(num < min){
min = diff
closest = x
}
}
closest
} //> getClosest: (num: Double, listNums: List[Double])Double
val closest = getClosest(num , listNums) //> closest : Double = 4.0
Therefore, to find out the closest number we just return the index of the found minimum in the given array indexArr. indexOf(min) .
In the Format values where this formula is true text box, please enter this formula: =ABS(A2-$D$2)=MIN(ABS($A$2:$A$15-$D$2)) (A2 is the first cell in your data list, D2 is the given number you will compare, A2:A15 is the number list you want to highlight the closest value from.)
This is almost a one-liner with minBy:
def getClosest(num: Double, listNums: List[Double]) =
listNums.minBy(v => math.abs(v - num))
minBy is unfortunately a partial function—it'll crash with an exception when called on an empty list. To match the behavior of your implementation, you can write the following:
def getClosest(num: Double, listNums: List[Double]) = listNums match {
case Nil => Double.MaxValue
case list => list.minBy(v => math.abs(v - num))
}
The problem with your code is that you're not taking the absolute value, as the other answer implicitly points out. Don't use Math.abs, though—that's shorthand for java.lang.Math.abs. math.abs is more idiomatic.
A simple implementation would be:
def getClosest(num : Double , list : List[Double]) :Double = list match {
case x :: xs => list.foldLeft(x){(ans,next) =>
if(math.abs(next - num) < math.abs(ans - num)) next else ans }
case Nil => throw new RuntimeException("Empty list")
}
scala> getClosest(20, List(1,19,22,24))
res0: Double = 19.0
A more general implementation would be:
def getClosest[A: Numeric](num: A, list: List[A]): A = {
val im = implicitly[Numeric[A]]
list match {
case x :: xs => list.minBy (y => im.abs(im.minus(y, num)))
case Nil => throw new RuntimeException("Empty list")
}
}
Thanks to @Travis for suggesting minBy. It is much more prettier than foldLeft
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With