Which happens first in flatMap, flatten or map?
Asked Answered
W

5

6

If I use xxx.flatMap(_.split(" ")), will it split the array an then flatten or will it flatten and then split?

Whitsuntide answered 12/5, 2016 at 5:8 Comment(0)
S
7

The purpose of flatmap functions is to take a function that returns a list, and then flatten the result.

So it will map the iterable (which splits in this case), then flatten the resulting 2D iterable (List in this case).

Strake answered 12/5, 2016 at 5:14 Comment(0)
E
3

Let's expand things a bit. First, let's define your xxx with examples of the sort that I think you intend:

val xxx = Array("hello there 马慧超", "how are you", "nice to meet you")

Now, let's write out your logic longer hand:

def words( str : String ) : Array[String] = str.split(" ")
xxx.flatMap( string => words(string) )

We start with an array of Strings. At some intermediate point, the function words is called on each of those immediate Strings, which yields Array[String], so conceptually we have a sequence of Arrays of String.

But we end up with just an array of Strings, "flattened" in that each of the words in the intermediate sequence of word arrays becomes part of one long array.

So, conceptually, first we execute that mapping function (words, or more directly split in this case), and then we flatten.

So, to answer your question directly, split then flatten.


Update (More, 'cuz why not?)

It's not even clear what it would mean to "flatten" a sequence of Strings, but informally you might imagine concatenation first. We can easily prove this is not what happens by instrumenting. If you try

def words( str : String ) : Array[String] = {println(str); str.split(" ")}
xxx.flatMap( string => words(string) )

you will see all of the individual Strings, not one concatenated String.

This is logically necessary for other types with flatMap methods. For

Some("there").flatMap( str => Some( str.toUpperCase ) )

there is no "flattening" you could perform on the Option Some(there) on which flatMap is called. "flattening" is only defined and meaningful once we have nested monadic contexts, i.e. when thinking about a hypothetical intermediate Some(Some(THERE)) value on which a flatten method would get called to produce Some(THERE).

Environ answered 12/5, 2016 at 5:43 Comment(1)
Thanks my friend,i got it,very appreciate you.Patton
K
3

Many good answers, but all are way too long :) It's very simple: before you split, there is nothing to flatten.

Kamikamikaze answered 12/5, 2016 at 10:31 Comment(0)
G
1

flatMap is a combination of map and then flatten. Example below can explain the process where we are calling map and then flatten and the result is a List[Char], while flatMap directly converts the Seq to List[Char].

val avengers = Seq("Ironman", "Thor", "Captain America")
val capsAvengers = avengers.map(_.toUpperCase)
println(avengers)
println(capsAvengers)
println(capsAvengers.flatten)
println(avengers.flatMap(.toUpperCase))

result:

List(Ironman, Thor, Captain America)

List(IRONMAN, THOR, CAPTAIN AMERICA)

List(I,R,O,N,M,A,N,T,H,O,R,C,A,P,T,A,I,N, ,A,M,E,R,I,C,A)

List(I,R,O,N,M,A,N,T,H,O,R,C,A,P,T,A,I,N, ,A,M,E,R,I,C,A)
Garamond answered 12/5, 2016 at 6:5 Comment(1)
Thanks I got it,very appreciatePatton
C
0

The flatMap can be saw as the combination of operation 'map' and 'flatten'.

As for your question, the answer is 'split' first and then 'flatten'.

The following example is use to illustrate this:

val nestedNumbers = List(List(1, 2), List(3, 4))

nestedNumbers.flatMap(x => x.map(_ * 2))

the output is

res0: List[Int] = List(2, 4, 6, 8)

, which is equivalent to the following code:

nestedNumbers.map((x: List[Int]) => x.map(_ * 2)).flatten

,its output is also

res0: List[Int] = List(2, 4, 6, 8)

the reference is there https://twitter.github.io/scala_school/zh_cn/collections.html#flatMap

have a good luck

Camphene answered 12/5, 2016 at 5:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.