Skip to content

Commit d34bffc

Browse files
committed
2.5 Reworked chapter
1 parent e3aea7e commit d34bffc

File tree

1 file changed

+27
-25
lines changed

1 file changed

+27
-25
lines changed

Part 2 - Sequence Basics/5. Transformation of sequences.md

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Transformation of sequences
22

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.
44

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.
66
* Ana(morphism) `T` --> `IObservable<T>`
77
* Cata(morphism) `IObservable<T>` --> `T`
88
* Bind `IObservable<T1>` --> `IObservable<T2>`
99

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.
1111

1212
```java
1313
class PrintSubscriber extends Subscriber{
@@ -32,7 +32,7 @@ class PrintSubscriber extends Subscriber{
3232

3333
### map
3434

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.
3636

3737
```java
3838
public final <R> Observable<R> map(Func1<? super T,? extends R> func)
@@ -57,12 +57,12 @@ Map: 6
5757
Map: Completed
5858
```
5959

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 string and 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.
6161

6262
```java
6363
Observable<Integer> values =
6464
Observable.just("0", "1", "2", "3")
65-
.map(s -> Integer.parseInt(s));
65+
.map(Integer::parseInt);
6666

6767
values.subscribe(new PrintSubscriber("Map"));
6868
```
@@ -76,11 +76,11 @@ Map: 3
7676
Map: Completed
7777
```
7878

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.
8080

8181
### cast and ofType
8282

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.
8484

8585
```java
8686
Observable<Object> values = Observable.just(0, 1, 2, 3);
@@ -115,7 +115,7 @@ Map: 2
115115
Map: Error: java.lang.ClassCastException: Cannot cast java.lang.String to java.lang.Integer
116116
```
117117

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.
119119

120120
```java
121121
Observable<Object> values = Observable.just(0, 1, "2", 3);
@@ -134,7 +134,7 @@ Map: Completed
134134

135135
### timestamp and timeInterval
136136

137-
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.
138138

139139
```java
140140
public final Observable<Timestamped<T>> timestamp()
@@ -159,13 +159,13 @@ Timestamp: Completed
159159

160160
The timestamp allows us to see that the items were emitted roughly 100ms apart (Java offers few guarantees on that).
161161

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.
163163

164164
```java
165165
public final Observable<TimeInterval<T>> timeInterval()
166166
```
167167

168-
We now use `timeInterval` in the same case as previously
168+
Using `timeInterval` in the same sequence as before:
169169

170170
```java
171171
Observable<Long> values = Observable.interval(100, TimeUnit.MILLISECONDS);
@@ -182,17 +182,19 @@ TimeInterval: TimeInterval [intervalInMilliseconds=100, value=2]
182182
TimeInterval: Completed
183183
```
184184

185+
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+
185187
### materialize and dematerialize
186188

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.
188190

189191
```java
190192
public final Observable<Notification<T>> materialize()
191193
```
192194

193195
![](https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/materialize.png)
194196

195-
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
196198

197199
```java
198200
Observable<Long> values = Observable.interval(100, TimeUnit.MILLISECONDS);
@@ -212,11 +214,11 @@ Materialize: Completed
212214

213215
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.
214216

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.
216218

217219
### flatMap
218220

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.
220222

221223
```java
222224
public final <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
225227

226228
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.
227229

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.
229231

230232
```java
231233
Observable<Integer> values = Observable.just(2);
@@ -241,7 +243,7 @@ flatMap: 1
241243
flatMap: Completed
242244
```
243245

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`.
245247

246248
```java
247249
Observable<Integer> values = Observable.range(1,3);
@@ -291,7 +293,7 @@ values
291293

292294
This example results in the entire alphabet being printed without errors, even though the initial range exceeds that of the alphabet.
293295

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.
295297

296298
```java
297299
Observable.just(100, 150)
@@ -324,7 +326,7 @@ We can see that the two observables are interleaved into one.
324326

325327
### concatMap
326328

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.
328330

329331
```java
330332
Observable.just(100, 150)
@@ -348,7 +350,7 @@ Output
348350
Completed
349351
```
350352

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`.
352354

353355
### flatMapIterable
354356

@@ -382,7 +384,7 @@ Output
382384

383385
As expected, the 3 iterables that we created are flattened in a single observable sequence.
384386

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.
386388

387389
There is a second overload to `flatMapIterable` that allows you to combine every value in the iterable with the value that produced the iterable.
388390

@@ -405,9 +407,9 @@ Output
405407

406408
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]`.
407409

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.
409411

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.
411413

412414
```java
413415
public static class Range implements Iterable<Integer> {
@@ -450,7 +452,7 @@ public static class Range implements Iterable<Integer> {
450452
}
451453
```
452454

453-
We can now iterate over a range and transform it without using a collection.
455+
We can now iterate over a range and transform it without the need to store anything.
454456

455457
```java
456458
Observable.range(1, 3)

0 commit comments

Comments
 (0)