I am new to Scala and searched for the same as to how can we change from Option[String] to a Map[String,trait] but could not find much .
The thing is I have a field of type Option[String] and I have to pass that value to a case class which takes input as a Map[String,User Defined trait].
That's what I want to know as to how can I convert the value of Option[String] to Map[]
Can we use Option.fold in this?
Could you please guide me further on this.
TIA
Consider using toMap as shown in the following example:
trait UDTrait[A] {
def len(s: A): Int
}
case class MyClass(m: Map[String, UDTrait[String]])
def myOptionToMap(opt: Option[String]): Map[String, UDTrait[String]] =
opt.map((_, new UDTrait[String]{ def len(s: String) = s.length })).toMap
MyClass(myOptionToMap(Some("a")))
// res1: MyClass = MyClass(Map(a -> $anonfun$myOptionToMap$1$$anon$1#10aabada))
MyClass(myOptionToMap(None))
// res2: MyClass = MyClass(Map())
Alternatively, you can use fold, as follows:
def myOptionToMap(opt: Option[String]): Map[String, UDTrait[String]] =
opt.fold(Map.empty[String, UDTrait[String]])(s =>
Map(s -> new UDTrait[String]{ def len(s: String) = s.length })
)
Option[ T ] is a container for zero or one element of a given type. An Option[T] can be either Some[T] or None object, which represents a missing value.
A Map is an Iterable consisting of pairs of keys and values. If you want to construct a map with one key value pair from an option which is non-empty, you can do the following:
trait Foo
def convertNonEmptyOptionToMap(a:Some[String], t: Foo): Map[String, Foo] = Map(a.get -> t)
That said, I don't completely understand what you're trying to do. More context with examples would be helpful.
Related
How to create an implicit converter so this code
case class Cookie(name: String, value: String)
val cookies: Seq[Cookie] = ???
val cookieMap: Map[String, String] = cookies.toMap
wil work? How to define implicit ev: A <:< (K, V)?
You can just do:
cookies.iterator.map(c => c.name -> c.value).toMap
To do the conversion in a single iteration. You may even hide that behind an extension method or something if you please.
Or you can provide an
implicit def Cookie2Tuple2(c: Cookie): (String, String) = c.name -> c.value
But that may blow up in other unexpected parts of the code.
I personally would define my own.
final case class Cookie(underlying: List[Cookie]) {
def toMap: Map[String, String] =
underlying.iterator.map(c => c.name -> c.value).toMap
}
From scala doc:
An instance of A <:< B witnesses that A is a subtype of B. Requiring
an implicit argument of the type A <:< B encodes the generalized
constraint A <: B.
That means that evidence Cookie <:< (K, V) means Cookie must be a subclass of (K,V). But it's not possible as tuple cannot be subclassed. Also, note that evidence are not implicit conversions, there are used to get guaranty at compile time but are not "applied".
(you can also read: https://stackoverflow.com/a/3427759/1206998)
Also, you can look at the implementation of toMap in IterableOnce
def toMap[K, V](implicit ev: A <:< (K, V)): immutable.Map[K, V] =
immutable.Map.from(this.asInstanceOf[IterableOnce[(K, V)]])
As you can see, it cast the iterable to an iterableOnce of tuple, no conversion of the collection's item is performed. Thus, entries should be effectively (K,V)
So the only thing you can do is add an implicit conversion from Seq[Cookie] to Map(String, String]
import scala.collection.breakOut // to directly convert to Map
case class Cookie(name: String, value: String)
implicit class Cookies(cookies: Seq[Cookie]) {
// use a self descriptive name, to avoid confusion
def toNameValueMap: Map[String, String] = {
cookies.map(c => (c.name, c.value))(breakout)
}
}
// example of use
val cookies: Seq[Cookie] = List(Cookie("chocolate", "good"), Cookie("hazelnut", "better"))
val cookieMap: Map[String, String] = cookies.toNameValueMap
And for the fun of it, here is the generic conversion method that correspond to what you expected toMap to be able to do:
implicit class SeqConversion[A](seq: Seq[A]) {
def convertToMap[K,V](getKey: A => K, getValue: A => V): Map[K,V] ={
seq.map(a => (getKey(a), getValue(a)))(breakOut)
}
}
cookies.convertToMap(_.name, _.value)
So I would like to use a generic return type and be able to use the info of that type within the function. Not sure this is possible but here is what I would like:
def getStuff[A](a: MyObj, b: String): Option[A] = {
// do some stuff
A match {
case String => Some(a.getString(b))
case Integer => Some(a.getInt(b))
...
case _ => None
}
}
However, as you know, A match is not a possibility. Any ideas on how I could achieve this ?
This is a classic case for using a typeclass:
trait StuffGetter[T] { // typeclass
def get(obj: MyObj, s: String): Option[T]
}
implicit val stringGetter = new StuffGetter[String] {
def get(o: MyObj, s: String): Option[String] = ???
}
implicit val intGetter = new StuffGetter[Int] {
def get(o: MyObj, s: String): Option[Int] = ???
}
def getStuff[A](a: MyObj, b: String)(implicit ev: StuffGetter[A]): Option[A] =
ev.get(a, b)
val stuff0 = getStuff[String](obj, "Hello") // calls get on stringGetter
val stuff1 = getStuff[Int](obj, "World") // call get on intGetter
val stuff2 = getStuff[Boolean](obj, "!") // Compile-time error
The StuffGetter trait defines the operations that you want to perform on the generic type, and each implicit value of that trait provides the implementation for a specific type. (For a custom type these are typically place in the companion object for the type; the compiler will look there for them)
When getStuff is called the compiler will look for an implicit instance of StuffGetter with the matching type. This will fail if no such instance exists, otherwise it will be passed in the ev parameter.
The advantage of this is that the "match" is done at compile time and unsupported types are also detected at compile time.
Conceptually we can differentiate between pattern matching at run-time which looks something like this
def getStuff[A](...) =
A match {
...
}
and pattern matching at compile-time which looks something like this
def getStuff[A](...)(implicit ev: Foo[A]) = {
ev.bar(...)
}
Key concept to understand is that types do not exists at run-time because they get "erased" after compilation so there is not enough information to pattern match on types once the program is running. However at compile-time, that is before the program runs, types do exist and Scala provides means to ask the compiler to effectively pattern match on them via implicit/givens mechanism which looks something like so
// type class requirements for type A
trait StringConverter[A] {
def getOptValue(b: String): Option[A]
}
// evidence which types satisfy the type class
implicit val intStringConverter: StringConverter[Int] = (b: String) => b.toIntOption
implicit val strStringConverter: StringConverter[String] = (b: String) => Some(b)
implicit def anyStringConverter[A]: StringConverter[A] = (b: String) => None
// compile-time pattern matching on type A
def getStuff[A](b: String)(implicit ev: StringConverter[A]): Option[A] = {
ev.getOptValue(b)
}
getStuff[Int]("3") // : Option[Int] = Some(value = 3)
getStuff[String]("3") // : Option[String] = Some(value = "3")
getStuff[Double]("3") // : Option[Double] = None
This compile-time pattern matching is called type class pattern.
Understanding the distinction between types and classes is one of the fundamental concepts in Scala https://docs.scala-lang.org/tutorials/FAQ/index.html#whats-the-difference-between-types-and-classes and gorking it will help understand how to write type classes.
Using custom typeclass similar to Getter:
trait KeyedGetter[S, K, A]:
def get(s: S, key: K): Option[A]
case class MyObj(ints: Map[String, Int], strs: Map[String, String])
object MyObj:
given KeyedGetter[MyObj, String, Int] with
def get(m: MyObj, k: String) = m.ints.get(k)
given KeyedGetter[MyObj, String, String] with
def get(m: MyObj, k: String) = m.strs.get(k)
def getStuff[A](m: MyObj, key: String)(using g: KeyedGetter[MyObj, String, A]): Option[A] =
g.get(m, key)
Using class tags:
case class MyObj(ints: Map[String, Int], strs: Map[String, String])
import reflect._
def getStuff[A](m: MyObj, key: String)(using ct: ClassTag[A]): Option[A] = (ct match
case _ if ct == classTag[String] => m.strs.get(key)
case _ if ct == classTag[Int] => m.ints.get(key)
case _ => None
).asInstanceOf[Option[A]]
If the erased types are insufficient, for a similar approach with type tags see this answer (and ignore the rest).
I have a map of type Map[_, Any], and I want to extract the values in their native format (without resorting to .asInstanceOf[_]).
Something like this...
val m: Map[String, Any] = Map("i" -> 1, "s" -> "test")
val i: Option[Int] = m.get("i")
val s: Option[String] = m.get("s")
Obviously that fails.
I don't like this approach, but I was thinking I could do something like this... but even this still comes out as Any instead of Int or String.
trait MyType[A] {
def value: A
}
implicit class MyInt(i: Int) extends MyType[Int] { def value: Int = i }
implicit class MyString(s: String) extends MyType[String] { def value: String = s }
val m: Map[String, MyType[_]] = Map("i" -> 1, "s" -> "test")
val i: Option[Int] = m.get("i").map(_.value)
val s: Option[String] = m.get("s").map(_.value)
Then I thought maybe some wrapper around the Map...
case class MyMap(m: Map[String, Any]) {
def get[A](k: String)(implicit ev: Option[Any] => Option[A]): Option[A] = m.get(k)
}
But that STILL comes out as Any. I just can't figure out how to convert Any => native.
So my questions are...
Why does this fail?
What are better way(s) to get the values out in their native format? Simplest and/or no external dependencies would be ideal... but I'm honestly open to anything (though caveat I'm still on scala 2.11 for now).
Thanks!
You cannot guess the runtime type for the reasons that have already been explained in the comments - this information is not there, once it's Any, all type information is lost, there is nothing you can do about it.
So, you'll have to provide the expected type yourself. How about an .as[T] helper method?
// This code is specifically for 2.11, please don't use it for more recent versions,
// see link below.
val m: Map[String, Any] = Map("i" -> 1, "s" -> "test")
import scala.reflect.{ ClassTag, classTag }
implicit class As(a: Any) {
def as[T](implicit ct: ClassTag[T]): Option[T] = ct.unapply(a)
}
println(m("i").as[Int].map(_ + 41).get)
println(m("s").as[String].map("This is a " + _).get)
This will print
42
This is a test
Brief explanation:
The As wrapper "pimps" all the objects, and attaches an .as method to everything.
The unapply does the checking, the casting, and the wrapping in an Option.
It will not work for generic types like List[Int] vs. List[String] etc, because this information is simply not available at runtime.
EDIT: Thanks #MarioGalic for greatly simplifying the solution.
I want Map to be implicit converted to java.utils.Properties.
implicit def map2Properties(map: Map[String, String]): Properties =
map.foldLeft(new java.util.Properties) { case (p, (k, v)) => p.put(k, v); p }
val properties :Properties = Option(Map[String,String]("k"->"v")).getOrElse(Map[String, String]())
Error:(7, 82) type mismatch;
found : Object
required: java.util.Properties
I got Map[String,String]() being converted first, so I got Object type in return.
Why not it to convert the both Map in Option and orElse together or convert the expression lazily in the end ?
How can I do to get it return Properties approprietly?
I am not exactly sure why the scala compiler cannot find the conversion for your Map. But I suspect it has something to do with (maybe) erasure and the signature of Option.getOrElse:
def getOrElse[B >: A](default: => B): B
The result is not necessarily the same type as the original option because it could also be a super type like Map[Any, Any].
There are several options how to work around this limitation:
Tell the compiler the result type of getOrElse explicitly:
val properties2: Properties = Option(Map[String,String](("k","v"))).getOrElse(Map.empty[String, String]): Map[String, String]
Use an intermediate step to tell the compiler that the option should contain Properties:
val propOpt: Option[Properties] = Option(Map[String,String](("k","v")))
val properties3: Properties = propOpt.getOrElse(Map.empty[String, String])
// or
val properties4: Properties = (Option(Map[String,String](("k","v"))): Option[Properties]).getOrElse(Map.empty[String, String])
// OR, shortest so far:
val properties5: Properties = Option(Map[String,String](("k","v")): Properties).getOrElse(Map.empty[String, String])
Something weird is going on with implicit conversion and return type of getOrElse.
However, this works:
Option(Map[String,String](("k","v"))).fold(Map[String,String]())(identity)
and so does this
Option(Map[String,String](("k","v"))).getOrElse[Map[String,String]](Map())
I want to be able to have a map implementation of type [String,Any] in which i can store objects of different classes then i want to be able to do
import app.User;
....
usr = map.getAs("user",User)// with usr being an object of Class/Object User. If user is not of that type it throws an exception.
where user was previously stored in the map.
Is this possible?
I can even be a standalone method e.g
usr = getAs(map,"user",User)
Thanks
You can use ClassTag. I don't have a lot of experience with it, but something like that should work:
def getAs[T: ClassTag](m: Map[String, Any], key: String): T =
m(key).asInstanceOf[T]
Usage:
val map = Map[String, Any]("one" -> 1, "hello"-> "world")
getAs[String](map, "hello")
res1: String = world
I let you deal with the exception, you can simply wrap the asInstanceOf in a Try, or whichever strategy you prefer. I also recommend changing the return type to Option[T].
Thanks vptheron. That definitely works.
I took your advice and did it using Option[T] and used matching instead.
def getAsOption[T: ClassTag](m: Map[String, Any], key: String): Option[T] =
m.get(key) match {
case x : Option[T] => x
case _ => None
}
I wanted a mechanism to pass random stuff in the play framework and this looks like a good solution for me though the purists wont like it.
I think using TypeTag will do better job unless you will never cast the values into generic types.
import scala.reflect.runtime.universe._
trait Typed[A] {
val value: A
implicit val ttag: TypeTag[A]
}
def cast[A, B: TypeTag](v: Typed[A]): Option[B] = {
val tA = typeOf[A](v.ttag)
val tB = typeOf[B]
def vB = v.value.asInstanceOf[B]
if (tA <:< tB) Some(vB) else None
}
def getTyped[A, B, C: TypeTag](map: Map[A, Typed[B]], key: A): Option[C] = {
map get key flatMap cast[B, C]
}