Skip to content
This repository was archived by the owner on Apr 20, 2018. It is now read-only.

Commit 15c15fb

Browse files
Adding more docs for transducers for Issue #327
1 parent d02d7f1 commit 15c15fb

File tree

4 files changed

+105
-6
lines changed

4 files changed

+105
-6
lines changed

doc/gettingstarted/transducers.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Transducers with Observable Sequences #
2+
3+
Much like Language Integrated Query (LINQ), Transducers are composable algorithmic transformations. They, like LINQ, are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element. Because transducers are decoupled from input or output sources, they can be used in many different processes - collections, streams, observables, etc. Transducers compose directly, without awareness of input or creation of intermediate aggregates. There are two major libraries currently out there, Cognitect's [`transduce.js`](https://github.com/cognitect-labs/transducers-js) and James Long's [`transduce-js`](https://github.com/jlongster/transducers.js) which are both great for getting high performance over large amounts of data. Because it is collection type neutral, it is a perfect fit for RxJS to do transformations over large collections.
4+
5+
The word `transduce` is just a combination of `transform` and `reduce`. The reduce function is the base transformation; any other transformation can be expressed in terms of it (`map`, `filter`, etc).
6+
```js
7+
var arr = [1, 2, 3, 4];
8+
9+
arr.reduce(
10+
function(result, x) { return result.concat(x + 1); }, []);
11+
12+
// => [ 2, 3, 4, 5 ]
13+
```
14+
15+
Using transducers, we can model the following behavior while breaking apart the map aspect of adding 1 to the concat operation, adding the seed and then the "collection" to transduce.
16+
17+
```js
18+
var arr = [1, 2, 3, 4];
19+
20+
function increment(x) { return x + 1; }
21+
function concatItem(acc, x) { return acc.concat(x); }
22+
23+
transduce(map(increment), concatItem, [], arr);
24+
25+
// => [ 2, 3, 4, 5 ]
26+
```
27+
28+
Using Cognitect's [`transduce.js`](https://github.com/cognitect-labs/transducers-js) library, we can easily accomplish what we had above.
29+
30+
```js
31+
var t = transducers;
32+
33+
var arr = [1, 2, 3, 4];
34+
35+
function increment(x) { return x + 1; }
36+
37+
into([], t.comp(t.map(increment)), arr);
38+
39+
// => [ 2, 3, 4, 5 ]
40+
```
41+
42+
We can go a step further and add filtering as well to get only even values.
43+
44+
```js
45+
var t = transducers;
46+
47+
var arr = [1, 2, 3, 4];
48+
49+
function increment(x) { return x + 1; }
50+
function isEven(x) { return x % 2 === 0; }
51+
52+
into([], t.comp(t.map(increment), t.filter(isEven)), arr);
53+
54+
// => [ 2, 4 ]
55+
```
56+
57+
Since it works so well using Arrays, there's no reason why it cannot work for Observable sequences as well. To that end, we have introduced the [`transduce`](https://github.com/Reactive-Extensions/RxJS/tree/master/doc/api/core/operators/transduce.md) method which acts exactly like it does for Arrays, but for Observable sequences. Once again, let's go over the above example, this time using an Observable sequence.
58+
59+
```js
60+
var t = transducers;
61+
62+
var source = Rx.Observable.range(1, 4);
63+
64+
function increment(x) { return x + 1; }
65+
function isEven(x) { return x % 2 === 0; }
66+
67+
var transduced = source.transduce(t.comp(t.map(increment), t.filter(isEven)));
68+
69+
transduced.subscribe(
70+
function (x) { console.log('Next: %s', x); },
71+
function (e) { console.log('Error: %s', e); },
72+
function () { console.log('Completed'); });
73+
74+
// => Next: 2
75+
// => Next: 4
76+
// => Completed
77+
```
78+
79+
Note that this above example also works the same with `transducers.js` as well with little to no modification. This example will in fact work faster than the traditional LINQ style (as of now) which most use currently.
80+
81+
```js
82+
var source = Rx.Observable.range(1, 4);
83+
84+
function increment(x) { return x + 1; }
85+
function isEven(x) { return x % 2 === 0; }
86+
87+
var transduced = source.map(increment).filter(isEven);
88+
89+
transduced.subscribe(
90+
function (x) { console.log('Next: %s', x); },
91+
function (e) { console.log('Error: %s', e); },
92+
function () { console.log('Completed'); });
93+
94+
// => Next: 2
95+
// => Next: 4
96+
// => Completed
97+
```
98+
99+
This opens up a wide new set of possibilities making RxJS even faster over large collections with no intermediate Observable sequences.

doc/readme.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ RxJS has no dependencies which complements and interoperates smoothly with both
1313
<th></th><th>Single return value</th><th>Mutiple return values</th>
1414
<tr>
1515
<td>Pull/Synchronous/Interactive</td>
16-
<td>`Object`</td>
17-
<td>Iterables (`Array` | `Set` | `Map`)</td>
16+
<td><pre><code>Object</code></pre></td>
17+
<td>Iterables(<pre><code>Array</code></pre> | <pre><code>Set</code></pre> | <pre><code>Map</code></pre>)</td>
1818
</tr>
1919
<tr>
2020
<td>Push/Asynchronous/Reactive</td>
21-
<td>`Promise`</td>
22-
<td>`Observable`</td>
21+
<td><pre><code>Promise</code></pre></td>
22+
<td><pre><code>Observable</code></pre></td>
2323
</tr>
2424
</table>
2525
</center>

src/core/linq/observable/concatmap.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* @returns {Observable} An observable sequence whose elements are the result of invoking the one-to-many transform function collectionSelector on each element of the input sequence and then mapping each of those sequence elements and their corresponding source element to a result element.
2828
*/
2929
observableProto.selectConcat = observableProto.concatMap = function (selector, resultSelector, thisArg) {
30-
if (resultSelector) {
30+
if (typeof selector === 'function' && typeof resultSelector === 'function') {
3131
return this.concatMap(function (x, i) {
3232
var selectorResult = selector(x, i);
3333
isPromise(selectorResult) && (selectorResult = observableFromPromise(selectorResult));

src/core/linq/observable/selectmany.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* @returns {Observable} An observable sequence whose elements are the result of invoking the one-to-many transform function collectionSelector on each element of the input sequence and then mapping each of those sequence elements and their corresponding source element to a result element.
2828
*/
2929
observableProto.selectMany = observableProto.flatMap = function (selector, resultSelector, thisArg) {
30-
if (resultSelector) {
30+
if (typeof selector === 'function' && typeof resultSelector === 'function') {
3131
return this.flatMap(function (x, i) {
3232
var selectorResult = selector(x, i);
3333
isPromise(selectorResult) && (selectorResult = observableFromPromise(selectorResult));

0 commit comments

Comments
 (0)