简体   繁体   中英

FlatMap behavior in scala

I am trying to get the hang of the flatMap implementation in Scala. Based on the definition in Scala programming

Function returning a list of elements as its right argument. It applies the function to each list and returns the concatenation of all function results.

Now to understand this, I have following implementations

val listwords = List(List("abc"),List("def"),List("ghi"))

val res2 = listwords flatMap (_+"1")
println(res2) //output- List(L, i, s, t, (, a, b, c, ), 1, L, i, s, t, (, d, e, f, ), 1, L, i, s, t, (, g, h, i, ), 1)

val res3 = listwords flatMap (_.apply(0).toCharArray())
println(res3) //output- List(a, b, c, d, e, f, g, h, i)

Looking at first output which drives me crazy, why is List[List[String]] treated like List[String] ?

After all with answer for above question, someone please help me to perform an operation which needs to pick the first character of the first string of each inner and result in a List[Char] . So given the listwords , I want the output to be List('a', 'd', 'g') .

List("abc") + "1" is equivalent to List("abc").toString + "1" so it returns the string "List(a, b, c)1". The type of List.flatMap is

flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): List[B]

and your function has type (List[String] => String) . String extends GenTraversableOnce[Char] so your result list has type List[Char] .

The code listwords flatMap (_+"1") can be rewritten as listwords flatMap (list => list.toString + "1") . So you basically transformed all lists to strings using toString method.

To obtain first characters you can use the following expression:

listwords.flatMap(_.headOption).flatMap(_.headOption)

_+"1" isn't doing what you think it's doing.

It's interpreted as list: List[String] => list.+("1")

Since List[String] doesn't contain such a method, the compiler looks for an implicit conversion in scope. It finds any2stringadd . (See http://docs.scala-lang.org/tutorials/tour/implicit-conversions for more on implicit conversions)

implicit final class any2stringadd[A](private val self: A) extends AnyVal {
  def +(other: String): String = String.valueOf(self) + other
}

list: List[String] => list.+("1")

now turns into

list: List[String] => new any2stringadd(list).+("1")

which returns String.valueOf(list) + "1"

First of all, you need to understand the difference between the map and the flatMap methods. Both of them iterate over some container and apply a function literal to every element. The difference is that the flatMap is making one more additional operation: it flattens the structure of the container. There is also a method that allows you just to do the flattening and its called flatten (so the flatMap is the equivalent of the map operation followed by the flatten operation). The second thing you have to remember is that you're modifying (mapping over) nested lists, so you need to nest your map / flatMap calls as well. Those examples should clarify all of those things to you:

scala> val wordLists = List(List("abc"),List("de"),List("f"), List())
wordLists: List[List[String]] = List(List(abc), List(de), List(f), List())

scala> val words = wordsLists.flatten
words: List[String] = List(abc, de, f)

scala> val replacedWordLists = wordsLists.map(_ => List("xyz"))
replacedWordLists: List[List[String]] = List(List(xyz), List(xyz), List(xyz), List(xyz))

scala> val replacedWords = wordsLists.map(_ => List("xyz")).flatten // Equivalent: wordsLists.flatMap(_ => List("xyz"))
replacedWords: List[String] = List(xyz, xyz, xyz, xyz)

scala> val upperCaseWordLists = wordsLists.map(_.map(_.toUpperCase))
upperCaseWordLists: List[List[String]] = List(List(ABC), List(DE), List(F), List())

scala> val upperCaseWords = wordsLists.map(_.map(_.toUpperCase)).flatten // Equivalent: wordsLists.flatMap(_.map(_.toUpperCase))
upperCaseWords: List[String] = List(ABC, DE, F)

scala> val optionalFirstLetterLists = wordLists.map(_.map(_.headOption))
optionalFirstLetterLists: List[List[Option[Char]]] = List(List(Some(a)), List(Some(d)), List(Some(f)), List())

scala> val optionalFirstLetters = wordLists.map(_.map(_.headOption)).flatten // Equivalent: wordLists.flatMap(_.map(_.headOption))
optionalFirstLetters: List[Option[Char]] = List(Some(a), Some(d), Some(f))

scala> val firstLetterLists = wordLists.map(_.map(_.headOption).flatten) // Equivalent: wordLists.map(_.flatMap(_.headOption))
firstLetterLists: List[List[Char]] = List(List(a), List(d), List(f), List())

scala> val firstLetters = wordLists.map(_.flatMap(_.headOption)).flatten // Equivalent: wordLists.flatMap(_.flatMap(_.headOption))
firstLetters: List[Char] = List(a, d, f)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM