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)
?
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)