Scala Map from Test File

Viewed 111

Looking to create a scala map from a test file. A sample of the text file (a few lines of it) can be seen below:

Alabama (9),Democratic:849624,Republican:1441170,Libertarian:25176,Others:7312
Alaska (3),Democratic:153778,Republican:189951,Libertarian:8897,Others:6904
Arizona (11),Democratic:1672143,Republican:1661686,Libertarian:51465,Green:1557,Others:475

I have been given the map buffer as follows:

var mapBuffer: Map[String, List[(String, Int)]] = Map()

Note the party values are separated by a colon.

I am trying to read the file contents and store the data in a map structure where each line of the file is used to construct a map entry with the date as the key, and a list of tuples as the value. The type of the structure should be Map[String, List[(String,Int)]].

Essentially just trying to create a map of each line from the file but I can't quite get it right. I tried the below but with not luck - I think that 'val lines' should be an array rather than an iterator.

val stream : InputStream = getClass.getResourceAsStream("")
      val lines: Iterator[String] = scala.io.Source.fromInputStream(stream).getLines
      var map: Map[String, List[(String, Int)]] = lines
        .map(_.split(","))
        .map(line => (line(0).toString, line(1).toList))
        .toMap
2 Answers

This appears to do the job. (Scala 2.13.x)

val stateVotes =
  util.Using(io.Source.fromFile("votes.txt")){
    val PartyVotes = "([^:]+):(\\d+)".r
    _.getLines()
     .map(_.split(",").toList)
     .toList
     .groupMapReduce(_.head)(_.tail.collect{
          case PartyVotes(p,v) => (p,v.toInt)})(_ ++ _)
  } //file is auto-closed

//stateVotes: Try[Map[String,List[(String, Int)]]] = Success(
// Map(Alabama (9) -> List((Democratic,849624), (Republican,1441170), (Libertarian,25176), (Others,7312))
//   , Arizona (11) -> List((Democratic,1672143), (Republican,1661686), (Libertarian,51465), (Green,1557), (Others,475))
//   , Alaska (3) -> List((Democratic,153778), (Republican,189951), (Libertarian,8897), (Others,6904))))

In this case the number following the state name is preserved. That can be changed.

No, iterator is fine (better than list actually), you just need to split the values too to create those tuples.

    lines
      .map(_.split(","))
      .map { case l => 
         l.head -> l.tail.toList.map(_.split(":"))
                     .collect { case Seq(a,b) => a -> b.toInt }
      }
      .toMap

An alternative that looks a little bit more aesthetic to my eye is converting to map early, and then using mapValues (I personally much prefer short lambdas). The downside is mapValues is lazy, so you end up having to do .toMap twice to force it in the end:

   lines
     .map(_.split(","))
     .map { case l => l.head -> l.tail.toList }
     .toMap
     .mapValues(_.split(":"))
     .mapValues(_.collect { case Seq(a,b) => a -> b.toInt })
     .toMap
Related