Listening to the Collections lecture from Functional Programming Principles in Scala, I saw this example:
scala> val s = \"Hello World\"
scala> s.flat
The reason for this behavior is that, in order to apply "map" to a String, Scala treats the string as a sequence of chars (IndexedSeq[String]). This is what you get as a result of the map invocation, where for each element of said sequence, the operation is applied. Since Scala treated the string as a sequence to apply map, that is what mapreturns.
flatMap then simply invokes flatten on that sequence afterwards, which then "converts" it back to a String
Use flatMap in situations where you run map followed by flattern. The specific situation is this:
• You’re using map (or a for/yield expression) to create a new collection from an existing collection.
• The resulting collection is a List of Lists.
• You call flatten immediately after map (or a for/yield expression).
When you’re in this situation, you can use flatMap instead.
Example: Add all the Integers from the bag
val bag = List("1", "2", "three", "4", "one hundred seventy five")
def toInt(in: String): Option[Int] = {
try {
Some(Integer.parseInt(in.trim))
} catch {
case e: Exception => None
}
}
Using a flatMap method
> bag.flatMap(toInt).sum
Using map method (3 steps needed)
bag.map(toInt) // List[Option[Int]] = List(Some(1), Some(2), None, Some(4), None)
bag.map(toInt).flatten //List[Int] = List(1, 2, 4)
bag.map(toInt).flatten.sum //Int = 7
You also have an interesting "collection of Scala flatMap examples", the first of which illustrates that difference between flatMap and map:
scala> val fruits = Seq("apple", "banana", "orange")
fruits: Seq[java.lang.String] = List(apple, banana, orange)
scala> fruits.map(_.toUpperCase)
res0: Seq[java.lang.String] = List(APPLE, BANANA, ORANGE)
scala> fruits.flatMap(_.toUpperCase)
res1: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E)
Quite a difference, right?
BecauseflatMaptreats aStringas a sequence ofChar, it flattens the resulting list of strings into a sequence of characters (Seq[Char]).
flatMapis a combination ofmapandflatten, so it first runsmapon the sequence, then runsflatten, giving the result shown.You can see this by running map and then flatten yourself:
scala> val mapResult = fruits.map(_.toUpperCase)
mapResult: Seq[String] = List(APPLE, BANANA, ORANGE)
scala> val flattenResult = mapResult.flatten
flattenResult: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E)
Your map function c => ("." + c) takes a char and returns a String. It's like taking a List and returning a List of Lists. flatMap flattens that back.
If you would return a char instead of a String you wouldn't need the result flattened, e.g. "abc".map(c => (c + 1).toChar) returns "bcd".
With map you are taking a list of characters and turning it into a list of strings. That's the result you see. A map never changes the length of a list – the list of strings has as many elements as the original string has characters.
With flatMap you are taking a list of characters and turning it into a list of strings and then you mush those strings together into a single string again. flatMap is useful when you want to turn one element in a list into multiple elements, without creating a list of lists. (This of course also means that the resulting list can have any length, including 0 – this is not possible with map unless you start out with the empty list.)