dlite922
dlite922

Reputation: 1994

How do I convert this ugly one liner into a clean scala for comprehension?

Hey guys I got this ugly thing:

val test = Some(Map("TesT",123))
val keys = test.getOrElse(Map()).keys.map(_.toLowerCase).asInstanceOf[Set[String]]
require(keys.contains("test")

Can I turn it (line #2) into a clean/readable for comprehension?

Here's my attempt:

scala> val keys = for {
     |       map <- test
     |       keys <- map.keys
     |       k <- keys
     | } yield k.toLowerCase
<console>:18: error: value toLowerCase is not a member of Char
       } yield k.toLowerCase
                 ^
<console>:16: error: type mismatch;
 found   : Iterable[Char]
 required: Option[?]
             keys <- map.keys
                  ^

Upvotes: 2

Views: 304

Answers (3)

Richard Sitze
Richard Sitze

Reputation: 8463

Your original:

val keys = for {
             map <- test
             keys <- map.keys
             k <- keys
       } yield k.toLowerCase

One constraint of the for comprehension over flatmap and map is that it requires the same kind of enumerator (really a monad) throughout, the first establishing which kind. In your example, the first enumerator is an Option, so it expects map.keys and keys to be of type Option as well.

Another problem with your example, is that keys is really a single key taken from the set map.keys... so k would then refer to a character in the enumurator across keys: String.

Resolve the for-comprehension type problem by converting the initial type of the for comprehension to a Seq, allowing the following terms to be of that type (they are), and then stop after extracting the key:

val keys = for {
        map <- test.toSeq
        key <- map.keys
    } yield key.toLowerCase

Upvotes: 1

Dima
Dima

Reputation: 40500

You don't need a for comprehension for this. Clearly sequencing operations, and spelling the steps out explicitly is often more clear and readable. Just don't try to cram things into a single line:

 test
   .iterator
   .flatMap(_.keys)
   .map(_.toLowerCase)
   .contains("test")

Upvotes: 3

kgeorgiy
kgeorgiy

Reputation: 1477

Comprehensions is just a syntactic sugar for map/flatMap. The flatMap method of Option returns another Option. To get a multiple values you should start with sequence, so the first line should be map <- test.toSeq. This explains the second error message.

The second line in the comprehension try to access keys using flatMap. It could be fixed in two ways. Either replaces it by val keys = map.keys, so neither map or flatMap is involved, or remove this line completely in make a call on the third one: k <- map.keys. This will fix fix the first error.

So you may choose between two solutions:

val keys = for {
       map <- test.toSeq
       val keys = map.keys
       k <- keys
 } yield k.toLowerCase

or

 val keys = for {
       map <- test.toSeq
       k <- map.keys
 } yield k.toLowerCase

Upvotes: 1

Related Questions