I'm trying to create a map from an option of list. So, I have an option of list declared like this:
val authHeaders: Option[Set[String]] = Some(Set("a", "b", "c"))
and I want to get a map like this: (a -> a, b -> b, c -> c).
So I tried this way:
for {
headers <- authHeaders
header <- headers
} yield (header -> header)
But I get this error:
<console>:11: error: type mismatch;
found : scala.collection.immutable.Set[(String, String)]
required: Option[?]
header <- headers
^
Where did I do wrong?
Additional note: this Option thing has been giving me quite a headache, but I need to understand how to deal with it in any case. Anyway, just for comparison, I tried removing the headache factor, by removing the Option.
scala> val bah = Set("a", "b", "c")
bah: scala.collection.immutable.Set[String] = Set(a, b, c)
scala> (
| for {
| x <- bah
| } yield (x -> x)).toMap
res36: scala.collection.immutable.Map[String,String] = Map(a -> a, b -> b, c -> c)
So, apparently it works. What is the difference here?
Additional note:
Looks like the rule of the game for the "for comprehension" here: if it produces something, that something must be of the same type of the outer collection (in this case that of authHeaders, which is an Option[?]). How to work around it?
Thanks!, Raka
The Problem
Your for
gets desugared into:
authHeaders.flatMap(headers => headers.map(header => header -> header))
The problem in this case is the usage of flatMap
, because authHeaders
is an Option
.
Lets have a look at the signature. (http://www.scala-lang.org/api/2.11.1/index.html#scala.Option)
final def flatMap[B](f: (A) ⇒ Option[B]): Option[B]
So the function f
is expected to return an Option
. But authHeaders.map(header => header -> header)
is not an Option
and therefore you get an error.
A solution
Assuming that if authHeaders
is None
you want an empty Map
, we can use fold
.
authHeaders.fold(Map.empty[String, String])(_.map(s => s -> s).toMap)
The first parameter is the result if authHeaders
is None
. The second is expected to be a function Set[String] => Map[String, String]
and gets evaluated if there is some Set
.
In case you want to keep the result in an Option
and just want to have a Map
when there actually is some Set
, you can simply use map
.
authHeaders.map(_.map(s => s -> s).toMap)
Regarding your additional Note
This is the signature of flatMap
on TraversableOnce
. (http://www.scala-lang.org/api/2.11.1/index.html#scala.collection.TraversableOnce)
def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): TraversableOnce[B]
Here f
can return any collection that is an instance of GenTraversableOnce
.
So things like this are possible: Set(1,2,3).flatMap(i => List(i))
(not really a creative example, I know..)
I see Option
as a special case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With