【问题标题】:Complex multi-dimensional list operations in ScalaScala 中复杂的多维列表操作
【发布时间】:2011-01-21 11:22:55
【问题描述】:

给定如下列表:

val dane = List(
    ("2011-01-04", -137.76),
    ("2011-01-04", 2376.45),
    ("2011-01-04", -1.70),
    ("2011-01-04", -1.70),
    ("2011-01-04", -1.00),
    // ... skip a few ...
    ("2011-12-22", -178.02),
    ("2011-12-29", 1800.82),
    ("2011-12-23", -83.97),
    ("2011-12-24", -200.00),
    ("2011-12-24", -30.55),
    ("2011-12-30", 728.00)
)

我想按指定顺序使用以下操作对特定月份(例如一月或01)的值(即内部列表的第二项)求和:

  1. groupBy
  2. slice
  3. collect
  4. sum

【问题讨论】:

  • 这不应该有“作业”标签吗?最好的解决方案是不要按照您指定的顺序使用确切的操作;以这种方式使用它们的唯一原因是作为家庭作业。
  • "按此顺序使用 groupBy、slice、collect、sum"... 似乎有点限制;那么现在谁在 Scala 中布置作业?
  • @Rex 以大约 1 秒的优势击败了我 :)
  • 什么是/是“all month in whay”?您是否要说明您需要列出 1 月份的所有值及其总和?
  • @Rex 这不是一个坏理由,也许我会稍微咬一下衬里......

标签: scala slice collect


【解决方案1】:

我感觉相反,所以这是一个不使用任何规定方法的答案:groupByslicecollectsum

避免collect 是最难的部分,condOpt/flatten 更丑陋...

val YMD = """(\d\d\d\d)-(\d\d)-(\d\d)""".r

import PartialFunction._

(dane map {
  condOpt(_:(String,Double)){ case (YMD(_,"01",_), v) => v }  
}).flatten reduceLeft {_+_}

【讨论】:

  • 它在 REPL 中有效,除非我错过了复制/粘贴中的某些内容
  • @Debilski 不,我没有,似乎对我来说很好用。你到底发现了什么错误?
  • 使用toMap,除了最后一天,您将丢失一天的所有值。
  • 哈!你是对的......我应该已经发现了,我过去什至故意使用过 :) +1 给你先生,相应地更新答案。
  • 我喜欢使用condOpt。我必须将它添加到我的曲目中。
【解决方案2】:
(for((YearMonthDay(_, 1, _), value)<-dane) yield value).sum

object YearMonthDay{
   def unapply(dateString:String):Option((Int, Int, Int)) ={ 
       //yes, there should really be some error checking in this extractor 
       //to return None for a bad date string
       val components = dateString.split("-")
       Some((components(0).toInt, components(1).toInt, components(2).toInt)) 
  }  

}

【讨论】:

  • 我认为这是一个方便的 unapply。
  • 是的,在我的大多数项目中都会出现这种情况,但不同之处在于它不会成为一个好的库
  • 您在YearMonthDay 上花费了太多时间。尝试val YearMonthDay = """(\d+)-(\d+)-(\d+)""".r 并在地图中使用"01" 而不是1
  • 很好,但您应该映射_.toInt 而不是应用三次。
  • 应该是def unapply (dateString: String): Option [(Int, Int, Int) ] = {,不应该是(带括号的选项,而不是括号)。
【解决方案3】:

现在凯文已经开始了相反答案的趋势,这是你永远不应该使用的一个,但是天哪,它有效! (并且避免了每个请求的方法,并且如果您更改字符串,它将在任何月份工作,但它确实要求列表按日期排序。)

dane.scanLeft(("2011-01",0.0))((l,r) =>
  ( l._1,
    if ((l._1 zip r._1).forall(x => x._1==x._2)) l._2+r._2 else 0.0
  )
).dropWhile(_._2==0).takeWhile(_._2 != 0.0).reverse.head._2

【讨论】:

  • 现在这确实是一件美丽的事情......它肯定会得到我的赞成!
  • 哈哈,好吧,如果它在 / 上,我会投票给“-1,愚蠢”。 (如果他们有一个愚蠢的标签)。
  • 为什么是dropWhile/takeWhile 而不是简单的filter
  • @Daniel - 当然,过滤器也可以工作。如果有多个块,我已经决定我想要 first 块,但没有理由选择它。我也没有很努力地写出一个最佳解决方案!
【解决方案4】:

将问题分解为更小的步骤。首先尝试将列表拆分为每个月的一个列表。您可以为此使用groupBy。您的第一个问题可能是如何解析日期字符串。一般的解决方案是使用自定义日期类和正则表达式;但是,在这种情况下,使用索引子字符串(或slice)的更简单的临时解决方案可能是合适的。

一般提示是将数据加载到 Scala REPL 中并使用它。祝你好运。

【讨论】:

    【解决方案5】:
    import scala.collection.mutable.HashMap
    val totals = new HashMap[Int, Double]
    for (e <- dane) {
        val (date, value) = e
        val month = date.drop(5).take(2).toInt
        totals(month) = totals.getOrElse(month,0.0) + value
    }
    

    另一个不使用任何建议的函数的实现,以及可变集合和一些程序和函数风格的混蛋,避免了一些有用的函数:)

    totals 最终成为从月份数到总数的映射。

    【讨论】:

    • 你正危险地接近一个有效的答案:)
    • 我恳求你的原谅:-P
    【解决方案6】:

    所以,这里有一个想法:

    • groupBy,因为您需要将每个月的数据组合在一起
    • slice,因为你需要查看日期是哪个月份
    • collect,因为您需要按月对filtermap 赋值
    • sum, mmmm...我不确定这个是从哪里来的。有什么想法吗?

    【讨论】:

      【解决方案7】:

      我拒绝混淆sum

      import org.joda.time.DateMidnight
      for (month <- 1 to 12) yield {
        dane map { case (d,v) => new DateMidnight(d).getMonthOfYear -> v }
        filter { case (m, v) => m == month }
        map (_._2)
        sum
      }
      

      【讨论】:

      • +1 用于使用 jodatime,这是迄今为止处理日期的最佳方式。
      【解决方案8】:
      dane.groupBy (_._1.matches (".*-01-.*")).slice (0, 1).map (x => x._2).flatten .map (y => y._2).sum
      

      我真的应该查找“收集”,它应该以某种方式替换我的地图/展平/地图。

      我的结果是:Double = 2234.29

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-13
        • 1970-01-01
        相关资源
        最近更新 更多