Converting a list to a map with foldLeft
Using the code below I am trying to create
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's the 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)) }
raises 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] encountered one error [error] (compilation: compileIncremental) Compilation error [error] Total time: 2s, completed 11-Jun-2017 22:51:46
Am I not using it correctly map
?
source to share
The problem you are having is with the anonymous function that you are trying to integrate with a new tuple in your map; what are you doing:
{ (m, s) => (m, List(s)) }
Where m
has a type Map[Date, List[(String , Float)]]
and s
has a type Details
.
The syntax (m, List(s))
means that you are creating a pair consisting of a map m
and a singleton list containing s
.
Instead, you have to put two elements in s
a new pair m
, which 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's going on here: You take the battery map m
and update it with each turn with s.date
a key followed by a value. The value is the previously stored value for this key ( m.get(s.date)
to make sure we don't overwrite this key) or an empty list, if the value is still missing, the value we are looking at is added while fold moves the collection.
This solves the problem, but as you can see, what you are doing is a well-known grouping operation and the Scala API Collection already provides you with the underlying infrastructure to accomplish your goal.
You can refactor your code like this and get 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 can see, I used a combinator groupBy
with parse
your formatting method . However, this function represents the final grouping values ββof the entire element, while you only need parts of it (which is why I used the combinator mapValues
).
If you are more interested in the order in which your card displays your objects, be sure to use a card that provides some order (for example SortedMap
).
source to share
Here's how to fix this 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, fold
you need to return an updated map m
.
To do this, you need to check if it contains m
s.date
. If yes, add a new one s.det
to the value of the existing list and put the updated list back on the map.
If this is the first occurrence s.date
, just create an empty list, put s.det
in it and then return the list m
.
Note that the values ββof the resulting Map can be in reverse order (since I am using cons ( ::
), which is more efficient than append for List
. You can reverse the resulting values ββwith map.mapValues(_.reverse)
).
source to share