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