【问题标题】:What's the idiomatic way to map producing 0 or 1 results per entry?映射每个条目产生 0 或 1 个结果的惯用方法是什么?
【发布时间】:2013-05-08 19:18:55
【问题描述】:

在每个条目产生 0 或 1 个结果的集合上调用 map 的惯用方式是什么?

假设我有:

val data = Array("A", "x:y", "d:e")

我想要的结果是:

val target = Array(("x", "y"), ("d", "e"))

(删除没有冒号的任何内容,在冒号上拆分并返回元组)

所以理论上我想我想做这样的事情:

val attempt1 = data.map( arg => {
    arg.split(":", 2) match {
      case Array(l,r) => (l, r)
      case _ => (None, None)
    }
  }).filter( _._1 != None )

我想做的是避免需要任何情况并摆脱filter

我可以通过预过滤来做到这一点(但我必须测试两次正则表达式):

val attempt2 = data.filter( arg.contains(":") ).map( arg => {
      val Array(l,r) = arg.split(":", 2)
      (l,r)
    })

最后,我可以使用 Some/None 和 flatMap...确实不需要 filter,但这是大多数 scala 程序员所期望的吗?

val attempt3 = data.flatMap( arg => {
     arg.split(":", 2) match {
       case Array(l,r) => Some((l,r))
       case _ => None
     }
})

在我看来,在 Scala 中会有一种惯用的方式来做到这一点,是吗?

【问题讨论】:

  • 你会想要.collect :) 虽然由于PartialFunction 的设计,它仍然会为每个元素运行你的正则表达式两次:/
  • 我收回“每个元素两次”的评论。看起来 Scala 现在在从匹配块构建其“神奇”部分函数时不会不必要地调用 unapply 变得更加聪明。我仍然讨厌PartialFunction 界面,但看起来他们现在跳过了一堆箍,尽可能减少它的愚蠢。

标签: scala


【解决方案1】:

使用Regex 提取器和collect :-)

scala> val R = "(.+):(.+)".r
R: scala.util.matching.Regex = (.+):(.+)

scala> Array("A", "x:y", "d:e") collect {
     |   case R(a, b) => (a, b)
     | }
res0: Array[(String, String)] = Array((x,y), (d,e))

编辑:

如果你想要一张地图,你可以这样做:

scala> val x: Map[String, String] = Array("A", "x:y", "d:e").collect { case R(a, b) => (a, b) }.toMap
x: Map[String,String] = Map(x -> y, d -> e)

如果性能是一个问题,您可以使用collection.breakOut,如下所示,以避免创建中间数组:

scala> val x: Map[String, String] = Array("A", "x:y", "d:e").collect { case R(a, b) => (a, b) } (collection.breakOut)
x: Map[String,String] = Map(x -> y, d -> e)

【讨论】:

  • 如果我想要一个 Map 而不是元组,我想在最后抛出一个 .toMap 是合理的吗?
  • @MarkElliot,是的。或者,如果性能是问题,您可以使用collection.breakOut 来避免在这种情况下创建中间数组。我会扩展我的答案。
猜你喜欢
  • 1970-01-01
  • 2014-02-27
  • 1970-01-01
  • 1970-01-01
  • 2018-12-21
  • 2011-05-28
  • 2013-07-04
  • 2011-05-14
  • 1970-01-01
相关资源
最近更新 更多