-
Notifications
You must be signed in to change notification settings - Fork 4
/
index.js
358 lines (234 loc) · 9.42 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
const { EMPTY, from, merge, zip } = require('rxjs');
const { distinct, take, filter, map, distinctUntilChanged, skip, takeLast, skipLast, concatMap, repeat } = require('rxjs/operators');
/**
* Helper function to display the data in console
* @param {any} data
*/
const toConveyorBelt = data => console.log(data);
// LEVEL: 1: subscribe
// Welcome to RxJS-fruits, where you write JavaScript / TypeScript code to mix your fruit juice!
// The basis of Reactive Extensions is the Observable. It is a counterpart of an array. Instead of that Retaining data it streams the data to interested subscribers. (
// An Observable is therefore only active if it is subscribed to with the subscribe-function.
// Recipe -
// In the source code we have an empty Observable with no data to expect.
// Move the conveyor belt, by subscribing to the empty observable with the subscribe function and click on the start button.
const conveyorBelt = EMPTY;
conveyorBelt.subscribe();
// LEVEL: 2: subscribe-next
// Now it's time to put some fruits on the conveyor belt!
// The from function creates an observable from an array. This delivers the data one after the other, like a foreach loop.
// The subscribe function expects a callback function as a parameter. This receives the data from the observable via the parameters.
// Recipe
// Subscribe to the Observable and put each fruit on the conveyor belt with the toConveyorBelt function.
// Add only the fruits that are specified on the recipe. (Note: use subscribe with next)
// Apple
// Banana
// Cherry
const fruits = from(["apple", "banana", "cherry"]);
fruits.subscribe(fruit => toConveyorBelt(fruit));
// LEVEL: 3 - distinct
// Exercise: distinct
// Oh no! We get more fruit than the recipe says.
// The pipe operator enables us to execute different RxJS operators one after the other in order to get our fruits as we need them.
// In our case, the distinct operator helps us. It prevents duplicate fruits from being delivered in our data stream.
// Recipe
// Each fruit should only be mixed once. (Note: use distinct)
// Apple
// Banana
const fruits = from(["apple", "apple", "banana", "apple"]);
fruits.pipe(
distinct()
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 4: take
// Exercise: take
// That is too much of a good thing!
// Use the take operator to put only as much bananas on the conveyor belt as is specified in the recipe.
// Recipe
// Only two bananas should be mixed. (Note: use take)
// Banana
// Banana
const fruits = from(['banana', 'banana', 'banana', 'banana']);
fruits.pipe(
take(2)
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 5: filter
// Exercise: filter
// Yuck! Old fruit has been delivered here.
// Use the filter operator to put only fresh fruit on the conveyor belt.
// All fresh apples and bananas should be mixed. (Note: use filter)
// Apple
// Apple
// Apple
// Banana
// Banana
// Banana
const fruits = from(['apple', 'apple', 'old-apple', 'apple', 'old-apple', 'banana', 'old-banana', 'old-banana', 'banana', 'banana']);
fruits.pipe(
filter(fruit => !fruit.includes('old'))
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 6 - map
// Exercise: map
// Some fruits are dirty in this delivery.
// The map operator enables data to be changed here. Wash the fruits by removing the dirt from dirty fruits
// Recipe
// All apples and bananas should be cleared of dirt. (Note: use map)
// Apple
// Apple
// Banana
// Banana
const fruits = from(['dirty-apple', 'apple', 'dirty-banana', 'banana']);
fruits.pipe(
map(fruit => {
if (fruit.includes('dirty')) return fruit.split('-')[1];
return fruit;
})
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 7 - filter, map, take
// What a mess!
// You just want an apple and a banana. However, the delivery also includes old and dirty fruit. Now use all previously learned operators filter, map and take one after the other in the pipe function.
// Add only the fruits that are specified on the recipe. (Note: use filter, map and take)
// Apple
// Banana
const fruits = from(['old-banana', 'apple', 'dirty-banana', 'apple']);
fruits.pipe(
filter(fruit => !fruit.includes('old')),
map(fruit => {
if (fruit.includes('dirty')) return fruit.split('-')[1]
return fruit
}),
take(2)
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 8 - distinctUntilChanged
// One after the other! Some fruits come twice in a row. However, we need the fruits alternately.
// In our case, the distinctUntilChanged operator helps us. It prevents duplicate fruits from being delivered one after the other in our data stream.
// No fruit should be mixed twice in a row. (Note: use distinctUntilChanged)
// Banana
// Apple
// Banana
const fruits = from(['banana', 'apple', 'apple', 'banana', 'banana']);
fruits.pipe(
distinctUntilChanged()
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 9 - skip
// Exercise: skip
// We can do without the first two fruits. The skip operator enables us to skip unnecessary fruit.
// Add only the fruits specified on the recipe. (Note: use skip)
// Banana
// Apple
const fruits = from(['apple', 'apple', 'banana', 'apple']);
fruits.pipe(
skip(2)
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 10 - skip-take-map
// Exercise: skip-take-map
// An excessive delivery!
// You just want a banana. However, the delivery contains too much unnecessary fruit. Now use all operators that have been learned so far skip, take and map one after the other in the pipe function.
// Add only the fruits that are specified on the recipe. (Note: use skip, take and map)
// Banana
const fruits = from(['dirty-apple', 'apple', 'dirty-banana', 'dirty-banana', 'apple']);
fruits.pipe(
skip(2),
take(1),
map(fruit => fruit.split('-')[1])
).subscribe(fruit => toConveyorBelt(fruit))
// LEVEL 11 - merge
// Exercise: merge
// Now we have to combine two deliveries.
// Our fruit supplier had to deliver the fruit to us separately. The merge function can combine different observables into one observable. Then we can use the pipe function to put only fresh fruit on the conveyor belt.
// Add only the fruits that are specified on the recipe. (Note: use merge and filter)
// Apple
// Apple
// Apple
// Banana
// Banana
// Banana
const apples = from(['apple', 'apple', 'old-apple', 'apple', 'old-apple']);
const bananas = from(['banana', 'old-banana', 'old-banana', 'banana', 'banana']);
merge(
apples,
bananas
).pipe(
filter(fruit => !fruit.includes('old'))
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 12 - takeLast
// Exercise: takeLast
// Only the last please!
// Use the takeLast operator to put only a certain number of the last fruits on the conveyor belt
// Only the last three fruits should be mixed. (Note: use takeLast)
// Banana
// Apple
// Banana
const fruits = from(['apple', 'apple', 'banana', 'apple', 'banana']);
fruits.pipe(
takeLast(3)
).subscribe(fruit => toConveyorBelt(fruit))
// LEVEL 13 - skipLast
// Exercise: skipLast
// We can do without the last two fruits.
// The skipLast operator enables us to ignore the last fruits
// Add only the fruits specified on the recipe. (Note: use skipLast)
// Apple
// Apple
// Banana
const fruits = from(['apple', 'apple', 'banana', 'apple', 'banana']);
fruits.pipe(
skipLast(2)
).subscribe(fruit => toConveyorBelt(fruit));
// LEVEL 14 -
// Exercise: skipLast, skip & merge
// Don't give chaos a chance!
// You received two deliveries. Take only usable fruit with the skipLast and skip operator. Then replace the empty observable with the merge function. Towards the end, clean up the dirty fruits and place them on the conveyor belt.
// Add only the fruits that are specified on the recipe. (Note: use skipLast, skip, merge and map)
// Apple
// Apple
// Apple
// Banana
// Banana
// Banana
const apples = from(['apple', 'dirty-apple', 'apple', 'old-apple', 'apple']);
const bananas = from(['old-banana', 'old-banana', 'dirty-banana', 'dirty-banana', 'dirty-banana']);
const freshApples = apples.pipe(
skipLast(2)
)
const freshBananas = bananas.pipe(
skip(2)
)
merge(
freshApples,
freshBananas
).pipe(
map(fruit => {
if (fruit.includes('dirty')) return fruit.split('-')[1]
return fruit
})
).subscribe(fruit => toConveyorBelt(fruit))
// LEVEL 15 -
// Exercise: zip & concatMap
// Variety is the key to happiness!
// We have received two deliveries and have to alternately put the contents on the conveyor belt. The zip function connects two observables and passes the data on to the pipe function alternately. However, not every fruit individually, but as a multi-dimensional array
// The concatMap operator projects the multidimensional array into an observable and returns the fruit individually.
// Each fruit should be mixed alternately. (Note: Use zip and concatMap)
// Apple
// Banana
// Apple
// Banana
const apples = from(['apple', 'apple']);
const bananas = from(['banana', 'banana']);
zip(
apples,
bananas
).pipe(
concatMap(([apple, banana]) => [apple, banana])
).subscribe(fruit => toConveyorBelt(fruit))
// LEVEL 16 -
// Exercise: repeat
// Much too little!
// The delivery really only brought us an apple. The recipe gives us three apples need. In this emergency, the repeat operator helps us. He repeats the observable with the specified number.
// Add only the fruits specified on the recipe. (Note: use repeat)
// Apple
// Apple
// Apple
const fruits = from(['apple']);
fruits.pipe(
repeat(3)
).subscribe(fruit => toConveyorBelt(fruit))