You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Part 2 - Sequence Basics/5. Transformation of sequences.md
+27-25Lines changed: 27 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,13 +1,13 @@
1
1
# Transformation of sequences
2
2
3
-
In this chapter we will see ways of changing the format of the data. In the real world, an observable may be of any type. It is very common that the values need to be expanded, trimmed to the relevant parts or simply replaced with something else.
3
+
In this chapter we will see ways of changing the format of the data. In the real world, an observable may be of any type. It is uncommon that the data is already in format that we want them in. More likely, the values need to be expanded, trimmed, evaluated or simply replaced with something else.
4
4
5
-
This will complete the three categories of operations. `map` and `flatMap` are the fundamental methods in the "bind" category. In literature, you will often bind them refered to as "bind", for reasons that are beyond the scope of this guide.
5
+
This will complete the three basic categories of operations. `map` and `flatMap` are the fundamental methods in the third category. In literature, you will often find them refered to as "bind", for reasons that are beyond the scope of this guide.
6
6
* Ana(morphism) `T` --> `IObservable<T>`
7
7
* Cata(morphism) `IObservable<T>` --> `T`
8
8
* Bind `IObservable<T1>` --> `IObservable<T2>`
9
9
10
-
In the last chapter we introduced an implementation of `Subscriber` for convenience. We will continue to use it in this example.
10
+
In the last chapter we introduced an implementation of `Subscriber` for convenience. We will continue to use it in the examples of this chapter.
11
11
12
12
```java
13
13
classPrintSubscriberextendsSubscriber{
@@ -32,7 +32,7 @@ class PrintSubscriber extends Subscriber{
32
32
33
33
### map
34
34
35
-
The basic method for transformation is `map` (also known as "select" in SQL-inspired systems like LINQ). It takes a function that takes an item and returns a new item of any type. The returned observable is composed of the values returned by the transformation function.
35
+
The basic method for transformation is `map` (also known as "select" in SQL-inspired systems like LINQ). It takes a transformation function which takes an item and returns a new item of any type. The returned observable is composed of the values returned by the transformation function.
36
36
37
37
```java
38
38
publicfinal<R>Observable<R> map(Func1<? super T,? extends R> func)
@@ -57,12 +57,12 @@ Map: 6
57
57
Map: Completed
58
58
```
59
59
60
-
This was something we could do without `map`, for example by using `Observable.range(3,4)`. In the following, we will do something more practical. We will emit numbers written as a stringand change them into the more processable integer format.
60
+
This was something we could do without `map`, for example by using `Observable.range(3,4)`. In the following, we will do something more practical. The producer will emit numeric values as a string, like many UIs often do, and then use `map` to convert them to a more processable integer format.
61
61
62
62
```java
63
63
Observable<Integer> values =
64
64
Observable.just("0", "1", "2", "3")
65
-
.map(s ->Integer.parseInt(s));
65
+
.map(Integer::parseInt);
66
66
67
67
values.subscribe(newPrintSubscriber("Map"));
68
68
```
@@ -76,11 +76,11 @@ Map: 3
76
76
Map: Completed
77
77
```
78
78
79
-
This transformation is simple enough that we could also do it withing the subscribe method. We changed the style of our examples a little bit to emphasise that mapping a value to another doesn't necessarily enable something otherwise impossible. An observable will often be the API between a component and its users. You could let the user do the parsing, or handle it yourself and present something much neater.
79
+
This transformation is simple enough that we could also do it on the subscriber's side, but that would be a bad division of responsibilities. When developing the side of the producer, you want to present things in the neatest and most convenient way possible. You wouldn't dump the raw data and let the consumer figure it out. In our example, since we said that the API produces integers, it should do just that. Tranfomation operators allow us to convert the initial sequences into the API that we want to expose.
80
80
81
81
### cast and ofType
82
82
83
-
`cast` is a shorthand for a transformation that only casts the items to a different type. If you had an `Observable<Object>` that you knew would only only emit values of type `T`, then it is just simpler to `cast` the observable, rather than do the casting in your lambda functions.
83
+
`cast` is a shorthand for the transformation of casting the items to a different type. If you had an `Observable<Object>` that you knew would only only emit values of type `T`, then it is just simpler to `cast` the observable, rather than do the casting in your lambda functions.
Map: Error: java.lang.ClassCastException: Cannot cast java.lang.String to java.lang.Integer
116
116
```
117
117
118
-
If you would rather have such cases ignored, you can you the `ofType` method. That works as a filter based on type and a subsequent cast.
118
+
If you would rather have such cases ignored, you can you the `ofType` method. This will filter our items that cannot be cast and then cast the sequence to the desired type.
The `timestamp` and `timeInterval` methods enable us to enrich our values with information with asynchronous nature of Rx. `timestamp` transforms values into the `Timestamped` type, which contains the original value, along with a timestamp for when the event was emitted.
137
+
The `timestamp` and `timeInterval` methods enable us to enrich our values with information about the asynchronous nature of sequences. `timestamp` transforms values into the `Timestamped<T>` type, which contains the original value, along with a timestamp for when the event was emitted.
138
138
139
139
```java
140
140
publicfinalObservable<Timestamped<T>> timestamp()
@@ -159,13 +159,13 @@ Timestamp: Completed
159
159
160
160
The timestamp allows us to see that the items were emitted roughly 100ms apart (Java offers few guarantees on that).
161
161
162
-
If we are more interested in how much time has passed since the last item, rather than the absolute moment in time that the items were emitted, we can use the `timeInterval` method.
162
+
If we are more interested in how much time has passed since the last item, rather than the absolute moment in time when the items were emitted, we can use the `timeInterval` method.
The information captured by `timestamp` and `timeInterval` is very useful for logging and debugging. It is Rx's way of aquiring information about the asynchronicity of sequences.
186
+
185
187
### materialize and dematerialize
186
188
187
-
`timestamp` and `timeInterval` are very useful for logging and debugging. Also useful for that purpose is `materialize`. `materialize` transforms a sequence into its metadata representation.
189
+
Also useful for logging is `materialize`. `materialize` transforms a sequence into its metadata representation.
The notification type can represent any event, not just the emission of values. Notice in the marble diagram that the emission of "onCompleted" did not mean the end of the sequence, as the sequence actually ends afterwards. Here's an example
197
+
The notification type can represent any event, i.e. the emission of a value, an error or completion. Notice in the marble diagram above that the emission of "onCompleted" did not mean the end of the sequence, as the sequence actually ends afterwards. Here's an example
The [Notification](http://reactivex.io/RxJava/javadoc/rx/Notification.html) type contains methods for determining the type of the event as well the carried value or `Throwable`, if any.
214
216
215
-
`dematerialize` will reverse the effects of `materialize`, returning the observable to its normal form.
217
+
`dematerialize` will reverse the effect of `materialize`, returning a materialized observable to its normal form.
216
218
217
219
### flatMap
218
220
219
-
`map` took one value and returned one other value in its place. `flatMap`'s transformation method produces an `Observable`for every value.
221
+
`map` took one value and returned another, replacing items in the sequence one-for-one. `flatMap` will replace an item with any number of items, including zero or infinite items. `flatMap`'s transformation method takes values from the source observable and, for each of them, returns a new observable that emits the new values.
220
222
221
223
```java
222
224
publicfinal<R>Observable<R> flatMap(Func1<? super T,? extends Observable<? extends R>> func)
@@ -225,7 +227,7 @@ public final <R> Observable<R> flatMap(Func1<? super T,? extends Observable<? ex
225
227
226
228
The observable returned by `flatMap` will emit all the values emitted by all the observables produced by the transformation function. Values from the same observable will be in order, but they may be interleaved with values from other observables.
227
229
228
-
Let's start with a simple example, where `flatMap` is applied on an observable with a single value. `values` will emit a single value, `2`. `flatMap` will turn it into an observable that is the range between `0` and `2`. The values by that observable are emitted in the final observable.
230
+
Let's start with a simple example, where `flatMap` is applied on an observable with a single value. `values` will emit a single value, `2`. `flatMap` will turn it into an observable that is the range between `0` and `2`. The values in this observable are emitted in the final observable.
229
231
230
232
```java
231
233
Observable<Integer> values =Observable.just(2);
@@ -241,7 +243,7 @@ flatMap: 1
241
243
flatMap: Completed
242
244
```
243
245
244
-
If`flatMap` is applied on an observable with multiple values, each value will produce a new observable. `values` will emit `1`, `2` and `3`. The resulting observables will respectively be emitting the values `[0]`, `[0,1]` and `[0,1,2]`. The values will be flattened together into one observable: the one that is returned by `flatMap`.
246
+
When`flatMap` is applied on an observable with multiple values, each value will produce a new observable. `values` will emit `1`, `2` and `3`. The resulting observables will emit the values `[0]`, `[0,1]` and `[0,1,2]`, respectively. The values will be flattened together into one observable: the one that is returned by `flatMap`.
This example results in the entire alphabet being printed without errors, even though the initial range exceeds that of the alphabet.
293
295
294
-
In our examples for `flatMap` so far, the values where in sequence: first all the values from the first observable, then all the values from the second observable. Though this seems intuitive, especially when coming from a synchronous environment, it is important to clarify that this is not the case. The observable returned by `flatMap` emits values as soon as they are available. It just happened that in our examples, all of the observables had all of their values ready immediately. To demonstrate, we construct asynchronous observables using the `interval` method.
296
+
In our examples for `flatMap` so far, the values where in sequence: first all the values from the first observable, then all the values from the second observable. Though this seems intuitive, especially when coming from a synchronous environment, it is important to note that this is not always the case. The observable returned by `flatMap` emits values as soon as they are available. It just happened that in our examples, all of the observables had all of their values ready synchronously. To demonstrate, we construct asynchronous observables using the `interval` method.
295
297
296
298
```java
297
299
Observable.just(100, 150)
@@ -324,7 +326,7 @@ We can see that the two observables are interleaved into one.
324
326
325
327
### concatMap
326
328
327
-
Even though `flatMap` shares its name with a very common operator in functional programming, it doesn't behave exactly like a functional programmer would expect. As we've already seen, `flatMap` may interleave the supplied sequences. The operator that won't mix the sequences is called `concatMap`, because it is related to the [concat](/Part%203%20-%20Taming%20the%20sequence/4.%20Combining%20sequences.md#concat) operator that we will see later.
329
+
Even though `flatMap` shares its name with a very common operator in functional programming, we saw that it doesn't behave exactly like a functional programmer would expect. `flatMap` may interleave the supplied sequences. There is an operator that won't interleave the sequences and is called `concatMap`, because it is related to the [concat](/Part%203%20-%20Taming%20the%20sequence/4.%20Combining%20sequences.md#concat) operator that we will see later.
328
330
329
331
```java
330
332
Observable.just(100, 150)
@@ -348,7 +350,7 @@ Output
348
350
Completed
349
351
```
350
352
351
-
We can see in the output that the two sequences are kept separate. Note that the `concatMap` operator only works with terminating sequences: it can't move on to the next sequence before the current sequence terminates. For that reason, we had to limit `interval`'s infinite sequence with `take(3)`.
353
+
We can see in the output that the two sequences are kept separate. Note that the `concatMap` operator only works with terminating sequences: it can't move on to the next sequence before the current sequence terminates. For that reason, we had to limit `interval`'s infinite sequence with `take`.
352
354
353
355
### flatMapIterable
354
356
@@ -382,7 +384,7 @@ Output
382
384
383
385
As expected, the 3 iterables that we created are flattened in a single observable sequence.
384
386
385
-
As an Rx developer, you are advised to present your data as observable sequences. However, when your data is already in the format of a collection, e.g. because standard Java operations returned them like that, it can be simpler to just use them as they are and not convert them first. In addition, `flatMapIterable` eliminates the need to make a choice about interleaving or not: `flatMapIterable` doesn't interleave.
387
+
As an Rx developer, you are advised to present your data as observable sequences and avoid mixing observables with iterables. However, when your data is already in the format of a collection, e.g. because standard Java operations returned them like that, it can be simpler or faster to just use them as they are without converting them first. `flatMapIterable`also eliminates the need to make a choice about interleaving or not: `flatMapIterable` doesn't interleave, just like you would expect from a synchronous flatMap.
386
388
387
389
There is a second overload to `flatMapIterable` that allows you to combine every value in the iterable with the value that produced the iterable.
388
390
@@ -405,9 +407,9 @@ Output
405
407
406
408
Here, we multiplied every value in the iterable range with the value that seeded the range: `[1*1]`, `[1*2, 2*2]`, `[1*3, 2*3, 3*3]`.
407
409
408
-
Java lacks a way to do `map` on its standard collections. It is therefore impossible to transform the iterable before the seeding value disappears (here, the `i` in `i -> range(1, i)`). If our iterable is just a list, we might be able to just modify the iterable before returning it. However, if our iterable isn't a collection, we would have to manually collect the modified values into a new collection and return that. We are using memory when we shouldn't have to. This overload of `flatMapIterable` saves us from having to insert this ugliness in the middle of our pipeline.
410
+
Java lacks a way to do `map` on its standard collections. It is therefore impossible to transform the iterable before the seeding value disappears (here, the `i` in `i -> range(1, i)`). Here, our iterable is just a list, so we could have just modified the iterable before returning it. However, if our iterable isn't a collection, we would have to either implement a `map` for iterables ourselves, or manually collect the modified values into a new collection and return that. This overload of `flatMapIterable` saves us from having to insert this ugliness in the middle of our pipeline.
409
411
410
-
The concept of laziness isn't very common in Java, so it may be unfamiliar to some readers. For the sake of example, consider the following iterable that generates a range lazily. It allows us to iterate over a range by calculating the next value from the previous one. In this way we save the memory of storing the whole range.
412
+
The concept of laziness isn't very common in Java, so you may be confused as to what kind of iterable isn't a collection. For the sake of example, consider the following iterable that generates a range lazily. It allows us to iterate over a range by calculating the next value from the previous one. In this way, we save the memory of storing the whole range.
0 commit comments