Difference between map and mapMany

1,462 views
Skip to first unread message

alex_ro_bv

unread,
Aug 25, 2013, 2:16:36 PM8/25/13
to rxj...@googlegroups.com
Hi guys,

I recently discovered this RxJava framework and it seems rather awesome. However, I find it hard to understand from the lack of examples, at least in java that is.

I have been towing around for a day or two, and the interesting part for me is the map function.

the RxJava version I use is 0.92.0-SNAPSHOT I think, and from examples and documentation I can understand that:
  •  - map function can apply those function I give it to async, being able to to give the result further and eventually I am able to subscribe to the result.
       - example of code I've tried: 
 Observable<String> o3 = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten").
                map((Func1<String,String>)p->"value_after_map-"+p).
                subscribeOn(Schedulers.newThread());
        o3.subscribe(v->System.out.println(v)); 
  •  - mapMany example:
           Observable<String> o2 = Observable.from("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten")
                .mapMany(new Func1<String, Observable<String>>() {

                    @Override
                    public Observable<String> call(final String v) {
                        return Observable.create(new Func1<Observer<String>, Subscription>() {

                            @Override
                            public Subscription call(final Observer<String> observer) {
                                observer.onNext("value_after_map-" + v);
                                observer.onCompleted();
                                return Subscriptions.empty();
                            }
                        }); // subscribe on a new thread
                    }
                }).subscribeOn(Schedulers.newThread());
        o2.subscribe(p->System.out.println(p));


The result is the same, but I clearly miss something. Both map and mapMany can be linked with more functions together. What is different? 

Thank you very much guys. :)

Prabir Shrestha

unread,
Aug 25, 2013, 2:36:28 PM8/25/13
to rxj...@googlegroups.com
Map takes T1 and returns T2. (transforms t1 into another object of type t2).
While MapMany takes Observable<T1> and returns T1 (flattens Observable<T1> to type T1)

You can find some good explanations at http://www.introtorx.com/content/v1.0.10621.0/08_Transformation.html (I highly recommend anyone using rx to read the entire tutorials.)
(The explanation is in .NET but applies for any Rx frameworks. map -> select and mapMany -> selectMany)

Here is a pseudo code to help understand.
public class Resource {
    public String url { get; set; }
    public String type { get; set; }
}

public Observable<File> downloadFile(String url);

Observable
    .from(resources)
    .map(r => r.url)
    .mapMany(url => downloadFile(url))
    .subscribe( file => { ... });

map convert Resource object to a Url which is of type String.
then mapMany takes Observable<File> and returns a File. (flattens and observable from Observable<T> to T)

You can also use the above without map.

Observable
    .from(resources)
    .mapMany(r => downloadFile(r.url))
    .subscribe( ... )

A better example of Map could be something like this.

downloadFile(url)
    .map(f => getByteArray(f)
    .subscribe( ...);

downloadFile(url)
    .map(f => getStream(f))
    .subscribe( ... );

rata...@gmail.com

unread,
Aug 28, 2013, 5:19:09 PM8/28/13
to rxj...@googlegroups.com
I agree with your definition of Map (takes T1 and returns T2. (transforms t1 into another object of type t2) but as far as I understand your definition of mapMany is incorrect:
mapmany takes an Observable<T1> and returns an Observable<Observable<T2>> which is then flattened into an Observable<T2> (a bit like a concatenation, i.e. for each T1, an array of T2 is returned for which each item are transmitted in a single sequence).

Matthias

unread,
Aug 29, 2013, 4:41:03 AM8/29/13
to rxj...@googlegroups.com
Here's an attempt to describe the map vs flatMap thing from a requirements perspective:

map: if you have an Observable that emits Ts, but your observer needs Rs instead, and there is a direct function f from T to R, then you can use map to transform all emitted Ts to Rs using f

flatMap: if you have an Observable that emits Ts, but your observer needs Rs instead, and there is no direct function that maps from T to R, but another Observable that does this job, then you can use flatMap/mapMany to transform Ts to Rs

Another way to think about flatMap is: if arriving from T to R (perhaps through intermediate T', T'', ...) involves expensive operations that need to be scheduled again, then flatMap is your guy. That's also why I prefer the name flatMap over mapMany, since the name makes the purpose more clear: it combines map and flatten, i.e. it maps your T to a sequence of sequence of Rs, and then flattens the sequences to a single sequence of R.

Gregory Benson

unread,
Nov 6, 2013, 5:00:45 PM11/6/13
to rxj...@googlegroups.com
For Rx fundamentals like this, there is a pretty good tutorial available here:

map() vs. mapMany() was something that I struggled with, however the information in the tutorial is helpful.  In exercise 13, we actually write the code for mapMany(), which you can see is simply a map operation followed by a mergeAll() operation.  In JavaScript:

Array.prototype.mapMany = function(projectionFunctionThatReturnsArray) {
    return this.
        map(function(item) {
            // Apply the projection function to each item. The projection
            // function will return a new child array. This will create a
            // two-dimensional array.
            return projectionFunctionThatReturnsArray(item)
        }).
        // apply the mergeAll function to flatten the two-dimensional array
        mergeAll();
};

Further, this exercise provides this example output, which is also helpful:

    var spanishFrenchEnglishWords = [ ["cero","rien","zero"], ["uno","un","one"], ["dos","deux","two"] ];
    // collect all the words for each number, in every language, in a single, flat list
    var allWords = [0,1,2].
        mapMany(function(index) {
            return spanishFrenchEnglishWords[index];
        });
    return JSON.stringify(allWords) === '["cero","rien","zero","uno","un","one","dos","deux","two"]';

If we had instead used the map() function instead of the mapMany() function in the example above, then allWords would simply equal the original spanishFrenchEnglishWords array since no merge operation would occur.

-Greg

Gregory Benson

unread,
Nov 6, 2013, 7:23:34 PM11/6/13
to rxj...@googlegroups.com

The corresponding source code in the RxJava project is here:

You can also see here that mapMany() is just a map() call followed by a merge() call.

Reply all
Reply to author
Forward
0 new messages