Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map versus FlatMap on String

Tags:

map

scala

Listening to the Collections lecture from Functional Programming Principles in Scala, I saw this example:

scala> val s = "Hello World"

scala> s.flatMap(c => ("." + c)) // prepend each element with a period
res5: String = .H.e.l.l.o. .W.o.r.l.d

Then, I was curious why Mr. Odersky didn't use a map here. But, when I tried map, I got a different result than I expected.

scala> s.map(c => ("." + c))
res8: scala.collection.immutable.IndexedSeq[String] = Vector(.H, .e, .l, .l, .o, 
                                                          ". ", .W, .o, .r, .l, 

I expected that above call to return a String, since I'm map-ing, i.e. applying a function to each item in the "sequence," and then returning a new "sequence."

However, I could perform a map rather than flatmap for a List[String]:

scala> val sList = s.toList
sList: List[Char] = List(H, e, l, l, o,  , W, o, r, l, d)

scala> sList.map(c => "." + c)
res9: List[String] = List(.H, .e, .l, .l, .o, ". ", .W, .o, .r, .l, .d)

Why was a IndexedSeq[String] the return type of calling map on the String?

like image 546
Kevin Meredith Avatar asked Oct 06 '13 13:10

Kevin Meredith


People also ask

Should I use map or flatMap?

You should use a map() if you just want to transform one Stream into another where each element gets converted to one single value. Use flatMap() if the function used by map operation returns multiple values and you want just one list containing all values.

What is the difference between map () and flatMap ()?

Both of the functions map() and flatMap are used for transformation and mapping operations. map() function produces one output for one input value, whereas flatMap() function produces an arbitrary no of values as output (ie zero or more than zero) for each input value. Where R is the element type of the new stream.

What is the difference between map () & flatMap () A map () does one to one transformation b flatMap () does one to many transformation C all the above?

Furthermore, map() and flatMap() can be distinguished in a way that map() generates a single value against an input while flatMap() generates zero or any number values against an input. In other words, map() is used to transform the data while the flatMap() is used to transform and flatten the stream.

Why do we need flatMap?

We can use a flatMap() method on a stream with the mapper function List::stream. On executing the stream terminal operation, each element of flatMap() provides a separate stream. In the final phase, the flatMap() method transforms all the streams into a new stream.


3 Answers

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

like image 89
fresskoma Avatar answered Oct 14 '22 11:10

fresskoma


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".

like image 41
ArtemGr Avatar answered Oct 14 '22 12:10

ArtemGr


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?
Because flatMap treats a String as a sequence of Char, it flattens the resulting list of strings into a sequence of characters (Seq[Char]).
flatMap is a combination of map and flatten, so it first runs map on the sequence, then runs flatten, 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)
like image 20
VonC Avatar answered Oct 14 '22 12:10

VonC