Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create Map from Option of List

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

like image 736
Cokorda Raka Avatar asked Jun 27 '14 01:06

Cokorda Raka


1 Answers

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.

like image 152
Kigyo Avatar answered Sep 30 '22 13:09

Kigyo