13

I can't understand the difference between map and flatMap In RxSwift. In the RxSwift playground examples and the books, flatMap is used as converting Observables which has inner Observable property.

However I see flatMap being used directly on Observable of basic types. For example for below code, both of them produces the same output. Can someone help me to understand the difference between map and flatMap

struct Student {
    let score:Int
}

let ryan = Student(score:80)
let student = PublishSubject<Student>()

let deneme = student.map({ val in
    return Student(score: val.score+10)
})
deneme.subscribe(onNext: {
    print("StudentMAP: \($0.score)")
})

let deneme2 = student.flatMap({ val -> Observable<Student> in
    return Observable.of(Student(score: val.score + 10))
})

deneme2.subscribe(onNext: {
    print("StudentFlatMAP: \($0.score)")
})

 student.onNext(ryan)
Meanteacher
  • 2,031
  • 3
  • 17
  • 48

3 Answers3

12

map get value from stream and return another value of whatever type, result is Observable< whatever type >.

flatMap get value from stream and return an Observable of whatever type.

This means you can use flatMap when:

  • you already have a function declared which returns Observable< ? >, so you may want to use it in flatMap

    func foo(_ number: Int) -> Observable<String> {
        return Observable.just(String(number))
    }
    
    Observable.just(1)
        .flatMap { (number) -> Observable<String> in
            return foo(number)
    }
    
  • you need that returned value push more than one value in the stream

    func updates() -> Observable<String> {
        // Something that generates updates
    }
    
    func shouldListenToUpdated() -> Observable<Bool> {
        return Observable.just(true)
    }
    
    shouldListenToUpdated()
        .flatMap { (listenToUpdated) -> Observable<String> in
            return listenToUpdated ? updates() : Observable.empty()
    }
    

While map will just transform next value in the stream.

Hope this clarifies things a bit.

10

To keep it simple Use flatMap when you want return Observable down the stream. Use map is simply transform the value of the observable and pass down the stream

Flatmap:

response.flatMap { response, _ -> Observable<NSString> in
        guard let value = response.allHeaderFields["string"] as? NSString
            else {
                return Observable.empty()
        }

        return Observable.just(value)
        }.subscribe(onNext: { [weak self]  string in
            print(string)
        }).disposed(by: bag)

Map:

 response.filter { response, _  in
            return 200..<300 ~= response.statusCode
            }.map { _ , data -> [[String: Any]] in

                guard let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
                    let result = jsonObject as? [[String: Any]] else {
                        return []
                }
                return result
            }.subscribe(onNext: { [weak self] objects in
                print(objects)
            }).disposed(by: bag)
Wasim
  • 921
  • 12
  • 14
1

flatMap is similar to map, but it transforms element of observable to an observable of sequences. The example you use is relatively simple, it is simply sending and Observable mapped into something else.

Here is quote from Reactive extension documentation,

The FlatMap operator transforms an Observable by applying a function that you specify to each item emitted by the source Observable, where that function returns an Observable that itself emits items. FlatMap then merges the emissions of these resulting Observables, emitting these merged results as its own sequence.

This method is useful, for example, when you have an Observable that emits a series of items that themselves have Observable members or are in other ways transformable into Observables, so that you can create a new Observable that emits the complete collection of items emitted by the sub-Observables of these items.

If you extend the example a bit, you will know that flatMap actually transforms each element into a sequence.

Notice that you used,

student.onNext(ryan)

Remove your dename2 and add this code below,

let studentObservable: PublishSubject<Student> = PublishSubject()

let deneme2 = student.flatMap({ val -> Observable<Student> in
    return studentObservable.map { val in Student(score: val.score + 10) }
})

deneme2.subscribe(onNext: {
    print("StudentFlatMAP: \($0.score)")
})

student.onNext(ryan)

studentObservable.onNext(Student(score: 80))
studentObservable.onNext(Student(score: 90))
studentObservable.onNext(Student(score: 100))

Now, you can see that map would simply transform a value from sequence and new Observable is created, while flatMap transforms it into sequence. Now, each of the flatMapped elements can themselves emit values since they are stream themselves.

Sandeep
  • 20,908
  • 7
  • 66
  • 106
  • Your code doesn't compile. Is `studentObservable` different than `student` Are you using previous student PublishSubject and flatMap to studentObservable. I really don't get it. – Meanteacher Apr 03 '18 at 11:25
  • Where doesnt it compile? studentObservable is a new PublishSubject, it is new sequence to intervene your flatMap for the same student – Sandeep Apr 03 '18 at 11:27
  • @Meanteacher See the changes, you should remove your dename2 and replace the code with the code above. – Sandeep Apr 03 '18 at 11:33
  • 1
    I changed the order, now it compiled. I still don't get it fully. For example if I used student publish subject, when I publish new items to subject both map and flatMap prints the same thing. Both of them are observable I don't understand the difference of observables of map and flatMap. I am going to read your answer again and again. Thanks. – Meanteacher Apr 03 '18 at 11:35