A collection of Scala ‘flatMap’ examples

Scala flatMap FAQ: Can you share some Scala flatMap examples?
Sure. When I was first trying to learn Scala, and cram the collections' flatMap method into my brain, I scoured books and the internet for great flatMap examples. Once I had a little grasp of it I started creating my own examples, and tried to keep them simple. I share those examples in this tutorial.


Using flatMap on a list of Strings

To get started, the following examples show the differences between map and flatMap on a Seq[String]:
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]). I like to think of flatMap as a combination of map followed by 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)
Tip: I often refer to flatMap as mapFlat so I can remember how it works.

Using a list of Options with map and flatMap

The following examples show more differences between map and flatMap for a simple String to Int conversion example. Given this toInt method:
def toInt(s: String): Option[Int] = {
    try {
        Some(Integer.parseInt(s.trim))
    } catch {
        // catch Exception to catch null 's'
        case e: Exception => None
    }
}
Here are a few examples to show how map and flatMap work on a simple list of strings that you want to convert to Int:
scala> val strings = Seq("1", "2", "foo", "3", "bar")
strings: Seq[java.lang.String] = List(1, 2, foo, 3, bar)

scala> strings.map(toInt)
res0: Seq[Option[Int]] = List(Some(1), Some(2), None, Some(3), None)

scala> strings.flatMap(toInt)
res1: Seq[Int] = List(1, 2, 3)

scala> strings.flatMap(toInt).sum
res2: Int = 6
flatMap does a nice job of flattening a list that has Some and None values in it.
Once again it’s worth noting that flatMap is equivalent to running map and then flatten:
scala> val mapResult = strings.map(toInt)
mapResult: Seq[Option[Int]] = List(Some(1), Some(2), None, Some(3), None)

scala> val flattenResult = mapResult.flatten
flattenResult: Seq[Int] = List(1, 2, 3)

flatMap with another function

The following code is not mine (see the URL below), but it does a great job of demonstrating flatMap when given the simple method g, where g returns three Int values when given one Int as input. That is, it transforms the single Int to the three resulting Ints:
scala> val list = List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)

scala> def g(v:Int) = List(v-1, v, v+1)
g: (v: Int)List[Int]

scala> list.map(x => g(x))
res0: List[List[Int]] = List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))

scala> list.flatMap(x => g(x))
res1: List[Int] = List(0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6)
This great example comes from the following URL:
See that page for more map and flatMap examples.

Convert Map values to a sequence with flatMap

Here's an interesting use of flatMap I just thought about. Although there are other ways to get the values from a Scala map, you can use flatMap for this purpose:
scala> val map = Map(1 -> "one", 2 -> "two", 3 -> "three")
map: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two, 3 -> three)

scala> 1 to map.size flatMap(map.get)
res0: scala.collection.immutable.IndexedSeq[java.lang.String] = Vector(one, two, three)
By contrast, notice what the map method gives you:
scala> 1 to map.size map(map.get)
res1: scala.collection.immutable.IndexedSeq[Option[java.lang.String]] = Vector(Some(one), Some(two), Some(three))
If you're new to Scala, note that the flatMap example is the same as this line of code, which may be more understandable:
1 to map.size flatMap(map.get(_))
Again, probably not ideal, but I'm just trying to throw different ideas out here.

flatMap examples from Twitter documentation

The folks at Twitter have put out some excellent Scala documentation, including a collection of flatMap examples that I've found in two different documents.
This first example invokes flatMap twice on a sequence of characters:
val chars = 'a' to 'z'
val perms = chars flatMap { a => 
  chars flatMap { b => 
    if (a != b) Seq("%c%c".format(a, b)) 
    else Seq() 
  }
}
Can you guess what perms looks like? Here is some of it:
perms: scala.collection.immutable.IndexedSeq[String] = Vector(ab, ac, ad, ae, af, ag, ah, ai, aj ...

// goes on until ... zw, zx, zy
They also show the following example of flatMap with Option. Remember that an Option is a container of 0 or 1 things, then guess what this code does:
val host: Option[String] = ..
val port: Option[Int] = ..

val addr: Option[InetSocketAddress] =
  host flatMap { h =>
    port map { p =>
      new InetSocketAddress(h, p)
    }
  }
Those two examples came from Twitter's Effective Scala document, which is an excellent doc.
This morning (Nov. 2, 2012), I saw the following additional flatMap examples in a new presentation by Marius:
Seq(1,2,3,4) flatMap { x =>
  Seq(x, -x)
}

// results in:
res0: Seq[Int] = List(1, -1, 2, -2, 3, -3, 4, -4)
Again this might be easier to understand if you look at map and then flatten:
scala> Seq(1,2,3,4) map { x =>
     |   Seq(x, -x)
     | }
res1: Seq[Seq[Int]] = List(List(1, -1), List(2, -2), List(3, -3), List(4, -4))

scala> res1.flatten
res2: Seq[Int] = List(1, -1, 2, -2, 3, -3, 4, -4)
Here's the second example from that presentation:
Seq(1,2,3,4) flatMap { x =>
  if (x%2 == 0) Seq(x)
  else Seq()
}

// results in:
res1: Seq[Int] = List(2, 4)

flatMap in the Play Framework

Here’s an example of flatMap being used in a Play Framework method:
flatMap example in the Play Framework
That example comes from this page. If you look at that page, you’ll see that the other examples use map, but this particular example uses flatMap. It’s an interesting exercise to look at those examples and wonder why this is.

Scala flatmap examples - Summary

I hope it helps to show some Scala flatMap examples, without too much discussion for the moment. In the end, flatMap is just a combination of map and flatten, so if map leaves you with a list of lists (or strings), add flatten to it. If that gives you what you need, call flatMap instead of map and flatten. After a while your brain will naturally think "flat map" without needing those intermediate steps.
If you have any flatMap examples you want to share, or improvements to the code shown, just leave a note in the Comments section.
Also, here's a repeat of those earlier links:

Commentaires

Enregistrer un commentaire

Posts les plus consultés de ce blog

Controlling Parallelism in Spark by controlling the input partitions by controlling the input partitions

Spark performance optimization: shuffle tuning

Spark optimization