Can we convert option[string] to caseclass in scala? - scala

When I tried to convert Some(string) into a case class I am getting an exception.
Ex:
val a:option[string]= Some("abc")
case class hello(a:string)
a.get.convertto[hello] => it is showing error

You need to allow for the Option being empty so avoid get and use getOrElse. Then just use the String to make the case class, like this:
val a: Option[String] = Some("abc")
case class hello(a: String)
val cc: hello = hello(a.getOrElse("default"))

The convertto you're looking for is map().
case class Hello(a: String)
val a: Option[String] = ??? //might be Some(s), might be None
val result: Option[Hello] = a.map(Hello)

Related

How to find class parameter datatype at runtime in scala

import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._
def getType[T: TypeTag](obj: T) = typeOf[T]
case class Thing(
val id: Int,
var name: String
)
val thing = Thing(1, "Apple")
val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature
dataType match {
case t if t =:= typeOf[Int] => println("I am Int")
case t if t =:= typeOf[String] => println("String, Do some stuff")
case _ => println("Absurd")
}
Not able to digest why result is Absurd instead of I am int?
My aim is to know data-type of class parameter at runtime and match it to predefined types.
Both current dataType and typeOf[Int] are printed as Int but if you do showRaw you'll see why they don't match
showRaw(dataType) // NullaryMethodType(TypeRef(ThisType(scala), scala.Int, List()))
showRaw(typeOf[Int]) // TypeRef(ThisType(scala), scala.Int, List())
The thing is that just the type Int and the type of nullary method returning Int are different types.
Try to add .resultType
val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature.resultType
dataType match {
case t if t =:= typeOf[Int] => println("I am Int")
case t if t =:= typeOf[String] => println("String, Do some stuff")
case _ => println("Absurd")
} // I am Int
It's also worth mentioning that .decl(TermName("id")) returns getter symbol, it's .decl(TermName("id ")) (with a blank space) that returns field symbol. So alternatively you can do with a blank space in the symbol name and without .resultType
val dataType = getType(thing).decl(TermName("id ")).asTerm.typeSignature
I'll add to #TomerShetah's answer that if the goal is "pattern matching" all fields of a case class then this can be done also at compile time (mostly) with Shapeless:
import shapeless.Poly1
import shapeless.syntax.std.product._
object printTypes extends Poly1 {
implicit val int: Case.Aux[Int, Unit] = at(t => println(s"I am Int: $t"))
implicit val string: Case.Aux[String, Unit] = at(t => println(s"String, Do some stuff: $t"))
implicit def default[V]: Case.Aux[V, Unit] = at(t => println(s"Absurd: $t"))
}
thing.toHList.map(printTypes)
// I am Int: 1
// String, Do some stuff: Apple
https://scastie.scala-lang.org/DmytroMitin/N4Idk4KcRumQJZE2CHC0yQ
#Dmytrio answer is a great explanation why the reflection didn't work as you expected.
I can understand from your question, that what you are trying to do, is actually pattern match all variables you have in a case class. Please consider doing it in the following way:
case class Thing(id: Int, name: String)
val thing = Thing(1, "Apple")
thing.productIterator.foreach {
case t: Int => println(s"I am Int: $t")
case t: String => println(s"String, Do some stuff: $t")
case t => println(s"Absurd: $t")
}
Code run at Scastie.

How to convert values of a case class into Seq?

I am new to Scala and I am having to provide values extracted from an object/case class into a Seq. I was wondering whether there would be any generic way of extracting values of an object into Seq of those values in order?
Convert the following:
case class Customer(name: Option[String], age: Int)
val customer = Customer(Some("John"), 24)
into:
val values = Seq("John", 24)
case class extends Product class and it provides such method:
case class Person(age:Int, name:String, lastName:Option[String])
def seq(p:Product) = p.productIterator.toList
val s:Seq[Any] = seq(Person(100, "Albert", Some("Einstain")))
println(s) //List(100, Albert, Some(Einstain))
https://scalafiddle.io/sf/oD7qk8u/0
Problem is that you will get untyped list/array from it. Most of the time it is not optimal way of doing things, and you should always prefer statically typed solutions.
Scala 3 (Dotty) might give us HList out-of-the-box which is a way of getting product's values without loosing type information. Given val picard = Customer(Some("Picard"), 75) consider the difference between
val l: List[Any] = picard.productIterator.toList
l(1)
// val res0: Any = 75
and
val hl: (Option[String], Int) = Tuple.fromProductTyped(picard)
hl(1)
// val res1: Int = 75
Note how res1 did not loose type information.
Informally, it might help to think of an HList as making a case class more generic by dropping its name whilst retaining its fields, for example, whilst Person and Robot are two separate models
Robot(name: Option[String], age: Int)
Person(name: Option[String], age: Int)
they could both represented by a common "HList" that looks something like
(_: Option[String], _: Int) // I dropped the names
If it's enough for you to have Seq[Any] you can use productIterator approach proposed by #Scalway. If I understood correctly you want also to unpack Option fields. But you haven't specified what to do with None case like Customer(None, 24).
val values: Seq[Any] = customer.productIterator.map {
case Some(x) => x
case x => x
}.toSeq // List(John, 24)
Statically typed solution would be to use heterogeneous collection e.g. HList
class Default[A](val value: A)
object Default {
implicit val int: Default[Int] = new Default(0)
implicit val string: Default[String] = new Default("")
//...
}
trait LowPriorityUnpackOption extends Poly1 {
implicit def default[A]: Case.Aux[A, A] = at(identity)
}
object unpackOption extends LowPriorityUnpackOption {
implicit def option[A](implicit default: Default[A]): Case.Aux[Option[A], A] = at {
case Some(a) => a
case None => default.value
}
}
val values: String :: Int :: HNil =
Generic[Customer].to(customer).map(unpackOption) // John :: 24 :: HNil
Generally it would be better to work with Option monadically rather than to unpack them.

Extractor object and class in Scala

I'm writing extractor object for functions expressions. Here is how it looks like:
object FunctionTemplate2 {
private final val pattern = Pattern.compile("^(.+?)\\((.+?)\\,(.+?)\\)")
//e.g. foo(1, "str_arg")
def unapply(functionCallExpression: String): Option[(String, String, String)] = {
//parse expression and extract
}
}
And I can extract as follows:
"foo(1, \"str_arg\")" match {
case FunctionTemplate2("foo", first, second) =>
println(s"$first,$second")
}
But this is not as cute as it could be. I would like to have something like that:
case FunctionTemplate2("foo")(first, second) =>
println(s"$first,$second")
Like curried extractor. So I tried this:
case class Function2Extractor(fooName: String){
private final val pattern = Pattern.compile("^(.+?)\\((.+?)\\,(.+?)\\)")
println("creating")
def unapply(functionCallExpression: String): Option[(String, String, String)] =
//parse and extract as before
}
But it did not work:
"foo(1, \"str_arg\")" match {
case Function2Extractor("foo")(first, second) =>
println(s"$first,$second")
}
Is there a way to do this in Scala?
You can simply it by using some utilities in Scala toolset
Notice how pattern is used in match case.
Scala REPL
scala> val pattern = "^(.+?)\\((.+?)\\,(.+?)\\)".r
pattern: scala.util.matching.Regex = ^(.+?)\((.+?)\,(.+?)\)
scala> "foo(1, \"str_arg\")" match { case pattern(x, y, z) => println(s"$x $y $z")}
foo 1 "str_arg"

case class product iterator to make string

I want to make a string representation of case class
case class Person(s:Student)
case class Student(name:String,value:String){
def toMyString(flag:Boolean):String= if(flag)s"${name} =${value}" else s"Hello${name}+${value}"
}
Person(Student("abc","def")).productIterator
I want to call toMyString from productIterator.
My actual use case has many elements in case class .
Just ask each iterator element if it is the target type.
Person(Student("abc","def")).productIterator.collect{
case x:Student => x.toMyString(true)
}
// res0: Iterator[String] = non-empty iterator (String: "abc=def")
There are 2 solutions for generating the String for the element:
1.Create a trait for elements that need to implement toMyString and the elements that need to generate string extends from it, and implement it, like:
trait MyString {
def toMyString(flag: Boolean): String
}
case class Student(name: String, value: String) extends MyString{
def toMyString(flag: Boolean): String = if (flag) s"${name} =${value}" else s"Hello${name}+${value}"
}
val result: List[String] = Person(Student("abc","def")).productIterator.collect{
case x: MyString => x.toMyString(true)
}
2. Use the reflection for this by get toMyString method by method name, this is a tricky way and not type safe, should think more about this, like:
val student = Student("abc", "def")
val res: Iterator[String] = Person(student).productIterator.map(i => {
val method = i.getClass.getDeclaredMethods.filter(i => i.getName.equals("toMyString")).headOption
method match {
case Some(m) =>
m.invoke(i, true.asInstanceOf[AnyRef])
case None =>
null
}
})
We need Iterator[Student] instead of Iterator[Any]
Person(Student("abc","def")).productIterator.asInstanceOf[Iterator[Student]]

construct case class from collection of parameters

Given:
case class Thing(a:Int, b:String, c:Double)
val v = Vector(1, "str", 7.3)
I want something that will magically create:
Thing(1, "str", 7.3)
Does such a thing exist (for arbitrary size Things)?
My first time dipping my toes into the 2.10 experimental reflection facilities. So mostly following this outline http://docs.scala-lang.org/overviews/reflection/overview.html, I came up with this:
import scala.reflect.runtime.{universe=>ru}
case class Thing(a: Int, b: String, c: Double)
object Test {
def main(args: Array[String]) {
val v = Vector(1, "str", 7.3)
val thing: Thing = Ref.runtimeCtor[Thing](v)
println(thing) // prints: Thing(1,str,7.3)
}
}
object Ref {
def runtimeCtor[T: ru.TypeTag](args: Seq[Any]): T = {
val typeTag = ru.typeTag[T]
val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
val classSymbol = typeTag.tpe.typeSymbol.asClass
val classMirror = runtimeMirror.reflectClass(classSymbol)
val constructorSymbol = typeTag.tpe.declaration(ru.nme.CONSTRUCTOR).asMethod
val constructorMirrror = classMirror.reflectConstructor(constructorSymbol)
constructorMirrror(args: _*).asInstanceOf[T]
}
}
Note that when I had the case class inside the main method, this did not compile. I don't know if type tags can only be generated for non-inner case classes.
I don't know if it's possible to get a working solution with a compile-time error, but this is my solution using matching:
case class Thing(a: Int, b: String, c: Double)
def printThing(t: Thing) {
println(t.toString)
}
implicit def vectToThing(v: Vector[Any]) = v match {
case (Vector(a: Int, b: String, c: Double)) => new Thing(a, b, c)
}
val v = Vector(1, "str", 7.3) // this is of type Vector[Any]
printThing(v) // prints Thing(1,str,7.3)
printThing(Vector(2.0, 1.0)) // this is actually a MatchError
Is there an actual purpose to this "Thing"-conversion or would you rather use Tuple3[Int,String,Double] instead of Vector[Any]?
From your question it's not clear what you will use it for. What you call a Thing might actually be a HList or a KList. HList stands for Heterogeneous Lists which is an "arbitrary-length tuple".
I am unsure how hard it would be to add an 'unnapply' or 'unapplySeq' method in order for it to behave more like a case class.
I have little experience with them, but a good explanation can be found here: http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/
If this is not what you need it might be a good idea to tell us what you want to achieve.