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.
Related
I am trying to convert a list to map in scala.
Input
val colNames = List("salary_new", "age_new", "loc_new")
Output
Map(salary_new -> salary, age_new -> age, loc_new -> loc)
Following code is working, but seems like I am over killing it.
val colRenameMap = colNames.flatMap(colname => Map(colname -> colname.split("_")(0))).toMap
I think map instead of flatMap would be more suitable for your case. Also you don't need to use the Map type internally, a single tuple should do the job.
For the sake of completeness this is how the definition of toMap looks like:
toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U]
as you can see the method expects a (T, U) which is a Tuple2.
Finally, two options using map:
// option 1: key/value
colNames.map{c => c -> c.split("_")(0)}.toMap
// option 2: tuple
colNames.map{c => (c, c.split("_")(0))}.toMap
I have a function in a context, (in a Maybe / Option) and I want to pass it a value and get back the return value, directly out of the context.
Let's take an example in Scala :
scala> Some((x:Int) => x * x)
res0: Some[Int => Int] = Some(<function1>)
Of course, I can do
res0.map(_(5))
to execute the function, but the result is wrapped in the context.
Ok, I could do :
res0.map(_(5)).getOrElse(...)
but I'm copy/pasting this everywhere in my code (I have a lot of functions wrapped in Option, or worst, in Either...).
I need a better form, something like :
res0.applyOrElse(5, ...)
Does this concept of 'applying a function in a concept to a value and immediatly returning the result out of the context' exists in FP with a specific name (I'm lost in all those Functor, Monad and Applicatives...) ?
You can use andThen to move the default from the place where you call the function to the place where you define it:
val foo: String => Option[Int] = s => Some(s.size)
val bar: String => Int = foo.andThen(_.getOrElse(100))
This only works for Function1, but if you want a more generic version, Scalaz provides functor instances for FunctionN:
import scalaz._, Scalaz._
val foo: (String, Int) => Option[Int] = (s, i) => Some(s.size + i)
val bar: (String, Int) => Int = foo.map(_.getOrElse(100))
This also works for Function1—just replace andThen above with map.
More generally, as I mention above, this looks a little like unliftId on Kleisli, which takes a wrapped function A => F[B] and collapses the F using a comonad instance for F. If you wanted something that worked generically for Option, Either[E, ?], etc., you could write something similar that would take a Optional instance for F and a default value.
You could write something like applyOrElse using Option.fold.
fold[B](ifEmpty: ⇒ B)(f: (A) ⇒ B): B
val squared = Some((x:Int) => x * x)
squared.fold {
// or else = ifEmpty
math.pow(5, 2).toInt
}{
// execute function
_(5)
}
Using Travis Browns recent answer on another question, I was able to puzzle together the following applyOrElse function. It depends on Shapeless and you need to pass the arguments as an HList so it might not be exactly what you want.
def applyOrElse[F, I <: HList, O](
optionFun: Option[F],
input: I,
orElse: => O
)(implicit
ftp: FnToProduct.Aux[F, I => O]
): O = optionFun.fold(orElse)(f => ftp(f)(input))
Which can be used as :
val squared = Some((x:Int) => x * x)
applyOrElse(squared, 2 :: HNil, 10)
// res0: Int = 4
applyOrElse(None, 2 :: HNil, 10)
// res1: Int = 10
val concat = Some((a: String, b: String) => s"$a $b")
applyOrElse(concat, "hello" :: "world" :: HNil, "not" + "executed")
// res2: String = hello world
The getOrElse is most logical way to do it. In regards to copy/pasting it all over the place - you might not be dividing your logic up on the best way. Generally, you want to defer resolving your Options (or Futures/etc) in your code until the point you need to have it unwrapped. In this case, it seems more sensible that your function takes in an an Int and returns an Int, and you map your option where you need the result of that function.
Let's say I have data like this:
scala> case class Foo(a: Int, b: Int)
defined class Foo
scala> val data: List[Foo] = Foo(1,10) :: Foo(2, 20) :: Foo(3,30) :: Nil
data: List[Foo] = List(Foo(1,10), Foo(2,20), Foo(3,30))
I know that in my data, there will be no instances of Foo with the same value of field a - and I want to transform it to Map[Int, Foo] (I don't want Map[Int, List[Foo]])
I can either:
scala> val m: Map[Int,Foo] = data.groupBy(_.a).mapValues(_.head)
m: Map[Int,Foo] = Map(2 -> Foo(2,20), 1 -> Foo(1,10), 3 -> Foo(3,30))
or:
scala> val m: Map[Int,Foo] = data.groupBy(_.a).map(e => e._1 -> e._2.head)(collection.breakOut)
m: Map[Int,Foo] = Map(2 -> Foo(2,20), 1 -> Foo(1,10), 3 -> Foo(3,30))
My questions:
1) How could I make the implementation with breakOut more concise / idiomatic?
2) What should I be aware of "under the covers" in each of the above-two solutions? I.e. hidden memory / compute costs. In particular, I am looking for a "layperson's" explanation of breakOut that does not necessarily involve an in-depth discussion of the signature of map.
3) Are there any other solutions I should be aware of (including, for example, using libraries such as ScalaZ)?
1) As pointed out by #Kigyo, the right answer, given that there are no duplicate as, wouldn't use groupBy:
val m: Map[Int,Foo] = data.map(e => e.a -> e)(breakOut)
Using groupBy is good when there could be duplicate as, but is totally unnecessary given your problem.
2) First, don't use mapValues if you plan on accessing values multiple times. The .mapValues method does not create a new Map (like the .map method does). Instead, it creates a view of a Map that recomputes the function (_.head in your case) every time it is accessed. If you plan on accessing things a lot, consider map{case (a,b) => a -> ??} instead.
Second, passing the breakOut function as the CanBuildFrom parameter does not incur additional costs. The reason for this is that the CanBuildFrom parameter is always present, just sometimes it's implicit. The true signature is this:
def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That
The purpose of the CanBuildFrom is to tell scala how to make a That out of the result of mapping (which is a collection of Bs). If you leave off breakOut, then it uses an implicit CanBuildFrom, but either way, there must be a CanBuildFrom so that there is some object that is able to build the That out of the Bs.
Finally, in your example with breakOut, the breakOut is completely redundant since groupBy produces a Map, so .map on a Map gives you back a Map by default.
val m: Map[Int,Foo] = data.groupBy(_.a).map(e => e._1 -> e._2.head)
I was browsing around and found a question about grouping a String by it's characters, such as this:
The input:
"aaabbbccccdd"
Would produce the following output:
"aaa"
"bbb"
"cccc"
"ddd"
and I found this suggestion:
val str = "aaabbbccccdd"[
val list = str.groupBy(identity).toList.sortBy(_._1).map(_._2)
And this identity fellow got me curious. I found out it is defined in PreDef like this:
identity[A](x: A): A
So basically it returns whatever it is given, right? but how does that apply in the call to groupBy?
I'm sorry if this is a basic question, is just that functional programming is still tangling my brains a little. Please let me know if there's any information I can give to make this question clearer
This is your expression:
val list = str.groupBy(identity).toList.sortBy(_._1).map(_._2)
Let's go item by function by function. The first one is groupBy, which will partition your String using the list of keys passed by the discriminator function, which in your case is identity. The discriminator function will be applied to each character in the screen and all characters that return the same result will be grouped together. If we want to separate the letter a from the rest we could use x => x == 'a' as our discriminator function. That would group your string chars into the return of this function (true or false) in map:
Map(false -> bbbccccdd, true -> aaa)
By using identity, which is a "nice" way to say x => x, we get a map where each character gets separated in map, in your case:
Map(c -> cccc, a -> aaa, d -> dd, b -> bbb)
Then we convert the map to a list of tuples (char,String) with toList.
Order it by char with sortBy and just keep the String with the map getting your final result.
To understand this just call scala repl with -Xprint:typer option:
val res2: immutable.Map[Char,String] = augmentString(str).groupBy[Char]({
((x: Char) => identity[Char](x))
});
Scalac converts a simple String into StringOps with is a subclass of TraversableLike which has a groupBy method:
def groupBy[K](f: A => K): immutable.Map[K, Repr] = {
val m = mutable.Map.empty[K, Builder[A, Repr]]
for (elem <- this) {
val key = f(elem)
val bldr = m.getOrElseUpdate(key, newBuilder)
bldr += elem
}
val b = immutable.Map.newBuilder[K, Repr]
for ((k, v) <- m)
b += ((k, v.result))
b.result
}
So groupBy contains a map into which inserts chars return by identity function.
First, let's see what happens when you iterate over a String:
scala> "asdf".toList
res1: List[Char] = List(a, s, d, f)
Next, consider that sometimes we want to group elements on the basis of some specific attribute of an object.
For instance, we might group a list of strings by length as in...
List("aa", "bbb", "bb", "bbb").groupBy(_.length)
What if you just wanted to group each item by the item itself. You could pass in the identity function like this:
List("aa", "bbb", "bb", "bbb").groupBy(identity)
You could do something silly like this, but it would be silly:
List("aa", "bbb", "bb", "bbb").groupBy(_.toString)
Take a look at
str.groupBy(identity)
which returns
scala.collection.immutable.Map[Char,String] = Map(b -> bbb, d -> dd, a -> aaa, c -> cccc)
so the key by which the elements are grouped by is the character.
Whenever you try to use methods such as groupBy on the String. It's important to note that it is implicitly converted to StringOps and not List[Char].
StringOps
The signature of groupBy is given by-
def groupBy[K](f: (Char) ⇒ K): Map[K, String]
Hence, the result is in the form -
Map[Char,String]
List[Char]
The signature of groupBy is given by-
def groupBy[K](f: (Char) ⇒ K): Map[K, List[Char]]
If it had been implicitly converted to List[Char] the result would be of the form -
Map[Char,List[Char]]
Now this should implicitly answer your curious question, as how scala figured out to groupBy on Char (see the signature) and yet give you Map[Char, String].
Basically list.groupBy(identity) is just a fancy way of saying list.groupBy(x => x), which in my opinion is clearer. It groups a list containing duplicate items by those items.
Consider this Map[String, Any]:
val m1 = Map(("k1" -> "v1"), ("k2" -> 10))
Now let's write a for:
scala> for ((a, b) <- m1) println(a + b)
k1v1
k210
So far so good.
Now let's specify the type of the second member:
scala> for ((a, b: String) <- m1) println(a + b)
k1v1
scala> for ((a, b: Integer) <- m1) println(a + b)
k210
Here, as I specify a type, filtering takes place, which is great.
Now say I want to use an Array[Any] instead:
val l1 = Array("a", 2)
Here, things break:
scala> for (v: String <- l1) println(v)
<console>:7: error: type mismatch;
found : (String) => Unit
required: (Any) => ?
My double question is:
why doesn't the second match filter as well?
is there a way to express such filtering in the second scenario without using a dirty isInstanceOf?
Well, the latter example doesn't work because it isn't spec'ed to. There's some discussion as to what would be the reasonable behavior. Personally, I'd expect it to work just like you. The thing is that:
val v: String = (10: Any) // is a compile error
(10: Any) match {
case v: String =>
} // throws an exception
If you are not convinced by this, join the club. :-) Here's a workaround:
for (va # (v: String) <- l1) println(v)
Note that in Scala 3, you can:
for (case v: String <- l1) println(v)
The main reason for the speced behavior is that we want to encourage people to add type annotations, for clarity. If in for comprehensions, they get potentially very costly filter operations instead, that's a trap we want to avoid. However, I agree that we should make it easier to specify that something is a pattern. Probably a single pair of parens should suffice.
val x: String = y // type check, can fail at compile time
val (x: String) = y // pattern match, can fail at run time
for (x: String <- ys) // type check, can fail at compile time
for ((x: String) <- ys) // pattern match, can filter at run time