Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the idiomatic way to map producing 0 or 1 results per entry?

Tags:

scala

What's the idiomatic way to call map over a collection producing 0 or 1 result per entry?

Suppose I have:

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

What I'd like as a result is:

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

(drop anything without a colon, split on colon and return tuples)

So in theory I think I want to do something like:

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

What I'd like to do is avoid the need for the any-case and get rid of the filter.

I could do this by pre-filtering (but then I have to test the regex twice):

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

Last, I could use Some/None and flatMap...which does get rid of the need to filter, but is it what most scala programmers would expect?

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

It seems to me like there'd be an idiomatic way to do this in Scala, is there?

like image 978
Mark Elliot Avatar asked Dec 16 '22 11:12

Mark Elliot


1 Answers

With a Regex extractor and 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))

Edit:

If you want a map, you can do:

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)

If performance is a concern, you can use collection.breakOut as shown below to avoid creation of an intermediate array:

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)
like image 192
missingfaktor Avatar answered Feb 15 '23 10:02

missingfaktor