blue-sky blue-sky -4 years ago 56
Scala Question

Converting List to Map with foldLeft

Using below code I'm attempting to produce

Map(2017-06-03 09:25:30 -> List( ("c",2190.79) , ("d",24.11), ("d",24.11), ("d",24.11) ),
2017-06-03 09:25:40 -> List( ("b",24.62) , ("b",24.62)) ,
2017-06-03 09:25:50 -> List( ("a",194.55) , ("a",194.55)) )


from

val l = List("a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30",
"a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30")


Here is complete code :

object Main extends App {

val l = List("a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30",
"a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30")

case class Details(date : java.util.Date , det : (String , Float))

val format = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")

val p = l.map(m => new Details(format.parse(m.split(",")(2)), ( m.split(",")(0),m.split(",")(1).toFloat) ))

val s = p.sortBy(r => (r.date))

val map = s.foldLeft(Map[java.util.Date, List[(String , Float)]]()) { (m, s) => (m , List(s)) }

}


Line

val map = s.foldLeft(Map[java.util.Date, List[(String , Float)]]()) { (m, s) => (m , List(s)) }


is throwing exception :


[error] found :
(scala.collection.immutable.Map[java.util.Date,List[(String, Float)]],
List[Main.Details]) [error] required:
scala.collection.immutable.Map[java.util.Date,List[(String, Float)]]
[error] val map = s.foldLeft(Mapjava.util.Date, List[(String ,
Float)]) { (m, s) => (m , List(s)) } [error]

^ [error] one error found [error] (compile:compileIncremental)
Compilation failed [error] Total time: 2 s, completed 11-Jun-2017
22:51:46


My use of map function is not implemented correctly ?

Answer Source

The problem you are facing comes from the anonymous function you are trying to integrate a new tuple into your map; what you do is:

{ (m, s) => (m, List(s)) }

Where m is of type Map[Date, List[(String , Float)]] and s is of type Details.

The (m, List(s)) syntax means that you are creating a pair composed of the map m and a singleton list that contains s.

What you want to achieve, instead, is to put the two items in s as a new pair of m, something that you can achieve by doing the following:

{ (m, s) => m.updated(s.date, s.det :: m.get(s.date).getOrElse(List.empty)) }

Let's see what happens here: you take the accumulator map m and update it at every turn of the fold with s.date as the key and then a value. The value is the previously held value for that key (m.get(s.date), to make sure we're not overwriting that key) or an empty list if there is still no value, prepended with the value that we are looking at right now while the fold traverses the collection.

This solves the issue, but as you may see, what you are doing is a widely known operation of grouping and the Scala Collection API already provides you the basic infrastructure to achieve your objective.

You can refactor your code like follows and obtain the same result:

object Main extends App {

  val l = List("a,194.55,2017-06-03 09:25:50",
    "b,24.62,2017-06-03 09:25:40",
    "c,2190.79,2017-06-03 09:25:30",
    "d,24.11,2017-06-03 09:25:30",
    "a,194.55,2017-06-03 09:25:50",
    "b,24.62,2017-06-03 09:25:40",
    "c,2190.79,2017-06-03 09:25:30",
    "d,24.11,2017-06-03 09:25:30")

  val format = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")

  val map = 
    l.groupBy(m => format.parse(m.split(",")(2))).
      mapValues(l => l.map(m => (m.split(",")(0),m.split(",")(1).toFloat)))

}

As you see, I've used the groupBy combinator with the parse method of your formatter. This function however presents as values of the resulting grouping the whole item, while you only wanted parts of it (which is why I further used the mapValues combinator).

If you are further interested into the ordering in which your map exposes your items, remember to use a map that enforces some kind of ordering (like a SortedMap).

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download