4

エントリごとに0または1の結果を生成するコレクションに対してマップを呼び出す慣用的な方法は何ですか?

私が持っているとします:

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 )

私がやりたいのは、any-case の必要性を避け、. を取り除くことfilterです。

事前にフィルタリングすることでこれを行うことができます (ただし、正規表現を 2 回テストする必要があります)。

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でこれを行うための慣用的な方法があるように私には思えますね。

4

1 に答える 1

12

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)
于 2013-05-08T19:24:53.277 に答える