Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic way to convert A => Seq[B]

Tags:

scala

I'd like to convert a single value into a collection of multiple "characteristics", without using a mutable data structure to collect the values. I'd like something like this fantasy construct which uses pattern matching, but does not stop after first match:

scala> 2 multimatch {
  case i if i > 0 => "Positive"
  case i if i < 0 => "Negative"
  case i if (i % 2 == 0) => "Even"
  //yadda yadda
}
res0: Seq[java.lang.String] = List(Positive, Even)
like image 468
Adam Rabung Avatar asked Jul 16 '11 21:07

Adam Rabung


3 Answers

Using the pimp pattern, partial functions and repeated parameters, this is how close I can get:

class MultiMatcher[A](a: A) {
  def multiMatch[B](pfs: PartialFunction[A, B]*): Seq[B] = {
    pfs.map(_.lift(a)).collect{ case Some(v) => v } 
  }
}

implicit def toMultiMatcher[A](a:A): MultiMatcher[A] = new MultiMatcher(a)

2 multiMatch (
  { case i if i > 0 => "Positive" },
  { case i if i < 0 => "Negative" },
  { case i if i % 2 == 0 => "Even" }
)
// returns Seq[java.lang.String] = ArrayBuffer(Positive, Even)
like image 93
huynhjl Avatar answered Nov 18 '22 18:11

huynhjl


First you define your characteristics as functions from Int to Option[String]

val pos = (i:Int) => if (i > 0) Some("Positive") else None
val neg = (i:Int) => if (i < 0) Some("Negative") else None
val even = (i:Int) => if (i % 2 == 0) Some("Even") else None

Then you make a list of characteristics.

val characteristics = pos::neg::even::Nil

And then you use flatmap to get a list of those characteristics that apply to a certain object.

scala> characteristics.flatMap(f=>f(2))
res6: List[java.lang.String] = List(Positive, Even)
like image 8
Kim Stebel Avatar answered Nov 18 '22 16:11

Kim Stebel


First, define a multimatch function as follows:

scala> def multimatch[A,B]( value : A,ps: ( A => Boolean, B)*) =
     |           for ( p <- ps
     |                 if (p._1(value))) yield p._2
multimatch: [A,B](value: A,ps: ((A) => Boolean, B)*)Seq[B]

Then, here we go:

    scala> multimatch(2,
         |           ( ((x :Int) => x > 0) -> "POSITIVE"),
         |           ( ((x :Int) => x < 0) -> "NEGATIVE"),
         |           ( ((x :Int) => x % 2 == 0) -> "EVEN")
         |       )

res4: Seq[java.lang.String] = ArrayBuffer(POSITIVE, EVEN)

Or, less cluttered:

scala> multimatch(2,
     |           ( (x :Int) => x > 0 , "POSITIVE"),
     |           ( (x :Int) => x < 0, "NEGATIVE"),
     |           ( (x :Int) => x % 2 == 0, "EVEN")
     |       )
res5: Seq[java.lang.String] = ArrayBuffer(POSITIVE, EVEN)
like image 2
anrizal - Anwar Rizal Avatar answered Nov 18 '22 17:11

anrizal - Anwar Rizal