Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find value closest to List of values?

Tags:

scala

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
like image 286
blue-sky Avatar asked Jan 08 '14 11:01

blue-sky


People also ask

How do you find the closest value to a number in an array?

Therefore, to find out the closest number we just return the index of the found minimum in the given array indexArr. indexOf(min) .

How do you highlight the closest value in a list to a given number in Excel?

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.)


2 Answers

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.

like image 88
Travis Brown Avatar answered Oct 05 '22 22:10

Travis Brown


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

like image 28
Jatin Avatar answered Oct 05 '22 23:10

Jatin