使用foldLeft将List转换为Map

时间:2017-06-11 21:56:15

标签: scala fold

使用下面的代码,我正在尝试生成

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

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

这是完整的代码:

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

}

行:

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

导致以下编译错误:

  

[错误]发现:   (scala.collection.immutable.Map [java.util.Date,List [(String,Float)]],   列表[Main.Details])[错误]要求:   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))} [错误]
  ^ [错误]发现一个错误[错误](编译:compileIncremental)   编译失败[错误]总时间:2秒,2017年6月11日完成   22时51分46秒

我没有正确使用map吗?

4 个答案:

答案 0 :(得分:3)

这不是例外,而是编译错误。该错误解释了您的代码有什么问题:

foldLeft的第二个参数(由错误消息中的^指出)必须是函数(B, A) ⇒ B。您的代码有(B, A) ⇒ (B, A)而不是......

答案 1 :(得分:2)

以下是修复该行的方法:

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)]()))
      )
}

对于fold的每次迭代,您需要返回更新的地图m

为此,您需要检查m是否已包含s.date。如果是,请将新s.det添加到现有列表值,并将更新后的列表重新添加到地图中。

如果这是第一次出现s.date,只需创建一个空列表,将s.det放入其中,然后将列表放回m

注意,生成的Map的值可能是相反的顺序(因为我使用cons(::)运算符,这比追加List更有效。你可以反转使用map.mapValues(_.reverse))得到的值。

答案 2 :(得分:2)

我认为可以更直接地实现目标。

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

答案 3 :(得分:1)

您遇到的问题来自您尝试将新元组集成到地图中的匿名函数;你做的是:

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

其中m的类型为Map[Date, List[(String , Float)]],而s的类型为Details

(m, List(s))语法表示您正在创建由地图m和包含s的单例列表组成的对。

您希望实现的目标是s中的两个项目作为一对m添加,这可以通过以下方式实现:

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

让我们看看这里发生了什么:你带着累加器地图m并在每次转弯时用s.date作为关键字再更新它,然后是一个值。该值是该键的先前保持值(m.get(s.date),以确保我们不会覆盖该键)或者如果仍然没有值则为空列表,前面加上我们正在查看的值而折叠遍历集合。

这解决了这个问题,但正如您所看到的,您正在做的是众所周知的分组操作,Scala Collection API已经为您提供了实现目标的基础架构。

您可以像下面那样重构代码并获得相同的结果:

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

}

如您所见,我已使用groupBy组合器和格式化程序的parse方法。然而,这个函数表示整个项目的结果分组值,而你只想要它的一部分(这就是我进一步使用mapValues组合子的原因)。

如果您对地图公开项目的顺序更感兴趣,请记住使用强制执行某种排序的地图(例如SortedMap)。