J. Doe
J. Doe

Reputation: 3

Scala error iterating through list of maps in for loop

I'm currently learning scala and am confused about an error I'm getting. I'm trying to play around with the different containers and currently I have a list of maps with each map sharing the same keys. I'm attempting to use a for loop to iterate through the list and put the values corresponding to the key "startlocation" into an array of type Node (class I made), but I can't seem to get the syntax right. It's telling me my key is an incorrect argument while trying to get the value at that key from the current map I'm at within the list.

What is wrong with this code and what should I do to fix it?

class Node{
     var visited = false;
     var mDistance = 10000;
     var prevLoc = " ";
}

object test{
  def main(args: Array[String]){

    var i = 0;
    var l = List(Map("startlocation" -> "Kruthika's abode", "endlocation" -> "Mark's crib", "distance" -> 10), 
                Map("startlocation" -> "Mark's Crib", "endlocation" -> "Kirk's Farm", "distance" -> 9));
   var b = new Array[Node](l.size);       
   var index = 0;
   for(i <- l){

       b(index).prevLoc = i("startlocation");    // This is the line where the error occurs
       index++;
    }


 }
}

Specific error: type mismatch; found : Any required: String

This error is highlighting the "startlocation" in the line I commented above.

Upvotes: 0

Views: 938

Answers (3)

Odomontois
Odomontois

Reputation: 16308

Errors

Heterogenous maps

First there is big trouble in definition of your map. Since you use Int and String as value type, resulting type is inferred as most possible concrete common supertype of those values, which is Any, since String and Int are pretty different.

Correct approach? It look like your maps are intented to have predefined set of keys, which in scala much better implemented via classes or even better case classes so near the Node class you can define

case class Route(startLocation: String, endLocation: String, distance: Int)

and later

val l = List(
  Route( startLocation = "Kruthika's abode",  endLocation = "Mark's crib", distance = 10),
  Route( startLocation = "Mark's Crib", endLocation = "Kirk's Farm", distance = 9))

or even

val l = List(
      Route( "Kruthika's abode", "Mark's crib",  10),
      Route( "Mark's Crib", "Kirk's Farm",  9))

and finally

b(index).prevLoc = i.startLocation    // This is the line where the error occurs

++

There is no such thing as postfix ++ operator in scala, so your index++ should be at least index += 1

Style guide

variables

Very often you may avoid var definition.

For instance var i = 0; is excessive since it's not even used in your code because in the for(i <- l) new i definition is created which is valid only inside the loop

Also you can avoid definition of var index, you can iterate over collection with index as

for((i, index) <- l.zipWithIndex)

Immutability

likewise you can very often avoid variables in your classes, making classes immutable. Instead of mutating you can create new instance (maybe .copy()ing exisitng) every time when you need.

This is crucial for you code, since b(index).prevLoc will definitely throw NullPointerException, because no instance of Node is created inside you array.

Imagine you defined

case class Node(visited: Boolean = false,  mDistance: Int  = 10000, prevLoc: String = " ")

And use is as

Node(prevLoc = i.startLocation)

for (list ≺ sequence ≺ monad) comprehensions

Finally your b could be now translated to val of immutable List since for is the expression that would create new collection from existing via yield. With such declaration you don't even need to know current index, so you can drop suggested .zipWithIndex part

;

And most finally - you can avoid ; at end of the line almost any time

Concluding, whole you code could be translated to

case class Node(visited: Boolean = false,  mDistance: Int  = 10000, prevLoc: String = " ")

case class Route(startLocation: String, endLocation: String, distance: Int)


object test{
  def main(args: Array[String]){

    val l = List(
      Route( "Kruthika's abode", "Mark's crib",  10),
      Route( "Mark's Crib", "Kirk's Farm",  9))

    val b = for(i <- l) yield Node(prevLoc = i.startLocation)
  }
}

you can add println(b) to end of main method to view how List and case classes could be printed

Upvotes: 5

Johny T Koshy
Johny T Koshy

Reputation: 3887

You could try this way,

val l = List(Map("startlocation" -> "Kruthika's abode", "endlocation" -> "Mark's crib", "distance" -> 10), 
            Map("startlocation" -> "Mark's Crib", "endlocation" -> "Kirk's Farm", "distance" -> 9));

case class Node(visited:Boolean = false, mDistance:Int = 10000, prevLoc:String)

l.map(x=>Node(prevLoc = x("startlocation").asInstanceOf[String]))

Upvotes: 1

codejitsu
codejitsu

Reputation: 3182

It's because you have mixed String and Int types in your maps. The 'prevLoc' seems to be a String, but your map contains Any (scala compiler infer this type, because you have not specified any).

I would suggest you to make your maps explicit like Map[String, String]. In this case your program will not compile (you have some ints as map values, sou you can do something like "distance" -> 10.toString).

P.S. try to avoid using var: it is possible to write Scala code without it, using immutable variables (vals) and functions like map, filter, flatMap in functional way.

Upvotes: 2

Related Questions