3
votes

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 causing the following compilation error:

[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

Am I not using map correctly?

4

4 Answers

3
votes

This is not an exception but a compilation error. The error explains what's wrong with your code:

The second argument of foldLeft (pointed by the ^ in the error message) must be a function (B, A) ⇒ B. Your code has a (B, A) ⇒ (B, A) instead...

2
votes

Here is how to fix that line:

val map = s.foldLeft(Map[java.util.Date, List[(String , Float)]]()) {
  (m, s) =>
    m +
      (s.date ->
        (s.det :: m.getOrElse(s.date, List[(String , Float)]()))
      )
}

For each iteration of fold you need to return updated Map m.

In order to do so you need to check if m already contains s.date. If yes, add new s.det to the existent list value and put updated list back into the map.

If this is the first occurrence of s.date, just create an empty list, put s.det into it and then put list back to m.

Note, that values of the resulting Map might be in reverse order (as i'm using cons (::) operator, which is more efficient than append for List. You can reverse resulting values using map.mapValues(_.reverse)).

2
votes

I think the objective can be achieved a little more directly.

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

l.map(_.split(","))
 .groupBy(a => format.parse(a(2)))
 .mapValues(_.map(a => (a(0),a(1).toFloat))) //Map[java.util.Date,List[(String, Float)]]
1
votes

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