Understanding Select

select is one of the most used operators in Rx and probably the easiest to grasp. Its intended purpose is to transform a value. It is also known as map

Examples with arrays

var a1 = [1, 3, 5];
var a2 = a1.map(function(val) { return val + 1; });
console.log('a1: ' + a1); // [1, 3, 5]
console.log('a2: ' + a2); // [2, 4, 6]

map and select act in the same way, except for with Rx, we assume its async.

Examples with Rx.

var Rx = require('rx');
var a1 = [1, 3, 5];
var a2 = Rx.
    Observable.
    from(a1).
    select(function(x) {
        return x + 1;
    });
console.log('a1: ' + a1); // [1, 3, 5]

// Now the special sauce of rx
// logs 2, 4, 6
a2.subscribe(function(x) {
    console.log(x);
});

Note: Subscribe would be invoked 3 times.

Going deeper.

To really understand the select operator, lets take apart the source code. I find that this really helps me in understanding new technology.

First here is the select source.

observableProto.select = observableProto.map = function (selector, thisArg) {
    var parent = this;
    return new AnonymousObservable(function (observer) {
        var count = 0;
        return parent.subscribe(function (value) {
            var result;
            try {
                result = selector.call(thisArg, value, count++, parent);
            } catch (exception) {
                observer.onError(exception);
                return;
            }
            observer.onNext(result);
        }, observer.onError.bind(observer), observer.onCompleted.bind(observer));
    });
};

Notice how select works. The select command subscribes to the parent (current Observable at the time of select) and passes the value through the provided selector. The selector is the function that is provided in the select command.

Watch out!

Re-look at the source, specifically the following line.

return parent.subscribe(function (value) { ... }

Notice that the subscribe call is made immediately when the result of the select call is subscribed to. So image the following situation.

var obs = Rx.Observable.returnValue(1);
for (var i = 0; i < 100000; i++) {
    obs = obs.select(function(x) {
        return x + 1;
    });
}

obs.subscribe(function() {
    console.log('I am so awesome!');
}, function(err) {
    console.log('Error: ' + err);
}, NOOP);

What is going to happen? Stack Overflow!!! Its all about the parent.subscribe statement. As the first subscribe happens, each parent will be subscribed to all the way up to the original observable (returnValue(1)). Which will generate a 100,000 stack frames. Don’t do this.

What if?

Something to think about of which will be addressed later is what if a select statement returns an observable? All the sudden you now have an Observable of Observables.

Mind Blown