Abhinav Rai

Map vs Flatmap (in reference to RxJava)

Lets start with basic understanding of map and flatmap with an example ->

val someList = listOf<Int>(1, 2, 3, 4)
val mapList = someList.map {
    x -> x*2
}
val flatMapList = someList.flatMap {
    x -> listOf<Int>(x * 2)
}

Both of them produce the same output -> [2, 4, 6, 8] The only difference comes between their signature.

More generically speaking:

fun <Y> map( fun (x): x): Y

fun <Y> flatMap (fun (x): Y ): Y

In the first example, Y isList<Int> and x is Int

Lets see the flatmap in javascript.

let arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]); 
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]

Its like, it flattens to one depth.

Now some advanced example. Flatmap in RxJava.

override fun articleDetail(id: String): Single<ArticleDetailData> {
    return articleEndPoint.getArticleDetail(id)
            .flatMap {
                val body = it.body()
                if (it.isSuccessful && body != null) {
                    Single.just(body.articleDetailData)
                } else {
                    Single.error(HttpException(it))
                }
            }
}

articleEndPoint.getArticleDetail(id) // returns Single<Response<ArticleDetailData> > when the api is hit.

Single<Something> calls .flatMap . So we do some logic with Something here and then we have to return the Single with Single.just or Single.error (to make them into Single) as its a flatmap.

Same implementation with map:-

override fun articleDetail(id: String): Single<ArticleDetailData> {
    return articleEndPoint.getArticleDetail(id)
            .map {
                val body = it.body()
                if (it.isSuccessful && body != null) {
                    body.articleDetailData
                } else {
                    error("Some HTTP Exception")
                }
            }
}

Here is a simple thumb-rule that I use help me decide as when to use flatMap() over map() in Rx's Observable.

Once you come to a decision that you’re going to employ a map transformation, you'd write your transformation code to return some Object right?

If what you’re returning as end result of your transformation is:

  1. a non-observable object then you’d use just map(). And map() wraps that object in an Observable and emits it.
  2. an Observable object, then you'd use flatMap(). And flatMap() unwraps the Observable, picks the returned object, wraps it with its own Observable and emits it.

Say for example we’ve a method titleCase(String inputParam) that returns Titled Cased String object of the input param. The return type of this method can be String or Observable<String>.

  1. If the return type of titleCase(..) were to be mere String, then you'd use map(s -> titleCase(s))
  2. If the return type of titleCase(..) were to be Observable<String>, then you'd use flatMap(s -> titleCase(s))