Map, FlatMap, Filter
Scala can create a list as followed.
val list = List(1, 2, 3)
Map
map
manipulates each element in the list.
val mappedList = list.map((element: Int) => element + 1)
println(mappedList) // List(2, 3, 4)
FlatMap
flatMap
flattens lists into one list.
val toPair = (element: Int) => List(element, element + 1)
val flatMappedList = list.flatMap(toPair)
println(flatMappedList) // List(1, 2, 2, 3, 3, 4)
Combining map
and flatMap
can replace nested loops. For example, we want a combination of two lists.
val chars = List('a', 'b', 'c')
val numbers = List(1, 2, 3)
val combinations = chars.flatMap(c => numbers.map(n => s"$c$n"))
println(combinations) // List(a1, a2, a3, b1, b2, b3, c1, c2, c3)
For Comprehensions
Although map
and flatMap
are powerful, they're hard to read. for-comprehension
improves readability.
val forCombinations = for {
c <- chars
n <- numbers
} yield s"$c$n"
println(forCombinations) // List(a1, a2, a3, b1, b2, b3, c1, c2, c3)
We can use filter
inside for-comprehension
, too.
val evenForCombinations = for {
c <- chars
n <- numbers if n % 2 == 0
} yield s"$c$n"
println(evenForCombinations) // List(a2, b2, c2)
It's equivalent to:
val evenCombinations = chars.flatMap(c => numbers.filter(n => n % 2 == 0).map(n => s"$c$n"))
println(evenCombinations)
Exercises
Create a collection which contains AT MOST one element.
abstract class Maybe[+T] {
def map[B](fn: T => B): Maybe[B]
def flatMap[B](fn: T => Maybe[B]): Maybe[B]
def filter(fn: T => Boolean): Maybe[T]
}
case object MaybeNot extends Maybe[Nothing] {
override def map[B](fn: Nothing => B): Maybe[B] = MaybeNot
override def flatMap[B](fn: Nothing => Maybe[B]): Maybe[B] = MaybeNot
override def filter(fn: Nothing => Boolean): Maybe[Nothing] = MaybeNot
}
case class Just[+T](value: T) extends Maybe[T] {
override def map[B](fn: T => B): Maybe[B] = Just(fn(value))
override def flatMap[B](fn: T => Maybe[B]): Maybe[B] = fn(value)
override def filter(fn: T => Boolean): Maybe[T] = {
if (fn(value)) this
else MaybeNot
}
}
The test results are as followed.
val just3 = Just(3)
println(just3) // Just(3)
println(just3.map(_ * 2)) // Just(6)
println(just3.flatMap(el => Just(s"$el is cool"))) // Just(3 is cool)
println(just3.filter(_ % 2 == 0)) // MayBeNothing