How to fill a ListBuffer[ List[Any] ] (Scala)?

385 views Asked by At

I have a ListBuffer declared like this:

var distances_buffer: ListBuffer[List[Any]] = ListBuffer.empty[List[Any]]

and I am trying to fill it with data like this:

for(current <- 0 to training_list_length - 1){
   //A and B are examples
   distances_buffer(current) ++= List[Any](A,B)
}

However I get the following error:

java.lang.IndexOutOfBoundsException: 0

What am I missing?

EDIT! More Info:

I have a list (Named: training_list) of points and their class. (x, y, class) :

training_list : List[((Double, Double, String))]

I also have an extra point with a x and a y value given.

My goal is to calculate the euclidean distance of the extra point from each point inside the training list, and create a result which look like this:

//example
List((x: Double, y: Double, class: String), distance: String)

List((4.3,3.0,Iris-setosa), 1.2529964086141665), (4.4,3.0,Iris-setosa), 1.341640786499874)...

As you can see, in the list I want to include the coordinates of the point (from the training_list), the class of the point and also the distance.

for(current <- 0 to training_list_length - 1){
    val dist = eDistance(test_x, test_y, training_x(current), training_y(current))
    distances_buffer += ListBuffer[Any](training_result(current),dist)
  }

After the creation of this list I want to sort it based on the distances. Also stuck here!

2

There are 2 answers

2
Mario Galic On BEST ANSWER

As suggested by Luis, Any and var are to be avoided if possible, so here is an example that might nudge you to consider a different approach

case class Point(x: Double, y: Double, `class`: String)

def distance(a: Point, b: Point): Double =
  math.hypot(a.x - b.x, a.y - b.y)

val targetPoint = Point(1,2,"Extra-point")
val training_list : List[((Double, Double, String))] = List((4.3,3.0,"Iris-setosa"), (4.4,3.0,"Iris-setosa"))
val points = training_list.map(Point.tupled)
val unsortedPoints: List[(Point, Double)] = points.map(point => (point, distance(point, targetPoint)))
unsortedPoints.sortBy(_._2)

which outputs

res0: List[(Point, Double)] = List((Point(4.3,3.0,Iris-setosa),3.4481879299133333), (Point(4.4,3.0,Iris-setosa),3.5440090293338704))

I have copied distance calculation from Xavier.

1
Luis Miguel Mejía Suárez On

It seems, for the way you name things, that you come from a Python background.

I would advice you to study a little bit about Scala first, since we are very different.
Not only on style things (like camel case vs dash case), but on more fundamental things like:

  • A strong and static type system. Thus, things like Any are usually a code smell and for 99.99% of cases, completely unnecessary.
  • A mix between OOP & FP. So, you do not necessary have to become an FP expert, but there some things that are idiomatic even in the OOP side of Scala, like immutability and common operations (higher order functions) like map, flatMap, filter & reduce.
  • Our List is very different from the Python one, accessing an element by index is O(n) (in Python it would be O(1)). A Python list is more like an Array that can be resized.
  • Also, we do not really have a for loop. We have something called for comprehension which is nothing more than syntactic sugar for calls to map, flatMap, filter & in some cases foreach.

The list could go on, but I think my point is clear.
Scala is not only new syntax, is a different way to program.


Anyways, here is an idiomatic way to solve your problem.
(Not necessary this is the best way, there are many variations that can be done)

// First lets create some custom types / classes to represent your data.
final case class Point(x: Double, y: Double)
final case class ClassifiedPoint(point: Point, clazz: String)

// Lets define the Euclidean distance function.
def euclideanDistance(p1: Point, p2: Point): Double =
  math.sqrt(
    math.pow((p1.x - p2.x), 2) +
    math.pow((p1.y - p2.y), 2)
  )
}

// This is what you need.
// Note that I made it somewhat more generic that is has to be.
// For example, instead of using the euclidean distance function directly on the body,
// we receive the distance function to use.
// Also, I use a technique called currying to split the arguments in different lists,
// This allows the caller to partially apply them.
def computeDistance(distanceFun: (Point, Point) => Double)
                   (trainingList: List[ClassifiedPoint])
                   (referencePoint: Point): List[(ClassifiedPoint, Double)] =
  trainingList.map { classifiedPoint =>
    val distance = distanceFun(classifiedPoint.point, referencePoint)
    classifiedPoint -> distance
  }

Which you can use like this.

val trainingList = List(
  ClassifiedPoint(Point(x = 4.3d, y = 3.0d), clazz = "Iris-setosa"),
  ClassifiedPoint(Point(x = 4.4d, y = 3.0d), clazz = "Iris-setosa")
)

// Partial application to create a new function.
val computeEuclideanDistance = computeDistance(euclideanDistance) _

computeEuclideanDistance(trainingList, Point(x = 3.0d, y = 0.0d))
// res: List[(ClassifiedPoint, Double)] =
//   List(
//     (ClassifiedPoint(Point(4.3, 3.0), "Iris-setosa"), 3.269556544854363),
//     (ClassifiedPoint(Point(4.4, 3.0), "Iris-setosa"), 3.3105890714493698)
//   )