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/4. Aggregation.md
+24-20Lines changed: 24 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,8 @@
1
1
# Aggregation
2
2
3
-
We've seen how to cut away parts of a sequence that we don't want, how to get single values, how to inspect the contents of sequence and how to deal with that information. Those things can be seen as reasoning about the containing sequence. Now we will see how we can use the raw data in the sequence to produce meaningful values.
3
+
We've seen how to cut away parts of a sequence that we don't want, how to get single values and how to inspect the contents of sequence. Those things can be seen as reasoning about the containing sequence. Now we will see how we can use the data in the sequence to derive new meaningful values.
4
4
5
-
The methods we will see here resemble what is called catamorphism. In our case, it would mean that the methods that all the values in the sequence and compose them into one. However, they do not strictly meet the definition, as they don't return a single value. Rather, they return an observable that promises to emit a single value.
5
+
The methods we will see here resemble what is called catamorphism. In our case, it would mean that the methods consume the values in the sequence and compose them into one. However, they do not strictly meet the definition, as they don't return a single value. Rather, they return an observable that promises to emit a single value.
6
6
7
7
If you've been reading through all of the examples, you should have noticed some repetition. To do away with that and to focus on what matters, we will now introduce a custom `Subscriber`, which we will use in our examples.
8
8
@@ -31,7 +31,7 @@ This is a very basic implementation that prints every event to the console, alon
31
31
32
32
### count
33
33
34
-
Our first method is `count`. It serves the same purpose as `length` and `size`, found in Java collections. This method will return an observable that waits until the sequence completes and emits the number of values in the source.
34
+
Our first method is `count`. It serves the same purpose as `length` and `size`, found in most Java containers. This method will return an observable that waits until the sequence completes and emits the number of values encountered.
@@ -72,15 +72,15 @@ Instead of getting a `java.util.NoSuchElementException`, you can use `firstOrDef
72
72
73
73
### last
74
74
75
-
`last` and `lastOrDefault` work in the same way, only the item returned is the last item before the sequence completed. When using the overload with a predicate, the item returned is the last item that matched the predicate.
75
+
`last` and `lastOrDefault` work in the same way as `first`, except that the item returned is the last item before the sequence completed. When using the overload with a predicate, the item returned is the last item that matched the predicate.
76
76
77
77
### single
78
78
79
79
`single` emits the only value in the sequence, or the only value that met predicate when one is given. It differs from `first` and `last` in that it does not ignore multiple matches. If multiple matches are found, it will emit an error. It can be used to assert that a sequence must only contain one such value.
@@ -107,14 +107,14 @@ The methods we saw on this chapter so far don't seem that different from the one
107
107
108
108
### reduce
109
109
110
-
You may have heard of `reduce` from MapReduce. Otherwise, you might have met it under the names "aggregate", "accumulate" or "fold". The general idea is that you produce a single value out of many by combining them two at a time. It its most basic overload, all you need is a function that combines two values into one.
110
+
You may have heard of `reduce` from MapReduce. Alternatively, you might have met it under the names "aggregate", "accumulate" or "fold". The general idea is that you produce a single value out of many by combining them two at a time. It its most basic overload, all you need is a function that combines two values into one.
This is best explained with an example. Here we will calculate the sum of a sequence of integers: 0+1+2+3+4=10 and separately the minimum value;
117
+
This is best explained with an example. Here we will calculate the sum of a sequence of integers: `0+1+2+3+4+...`. We will also calculate the minimum value for a different example;
`reduce` in Rx is not identical to "reduce" in parallel systems. In the context of parallel systems, it implies that the pairs of values can be choosen arbitrarily so that multiple machines can work independently. In Rx, the `accumulator` function is applied in sequence from left to right (as seen on the marble diagram). Each time, the accumulator function combines the result of the previous accumulations with the next value. This is more obvious in the other overload:
138
+
`reduce` in Rx is not identical to "reduce" in parallel systems. In the context of parallel systems, it implies that the pairs of values can be choosen arbitrarily so that multiple machines can work independently. In Rx, the `accumulator` function is applied in sequence from left to right (as seen on the marble diagram). Each time, the accumulator function combines the result of the previous step with the next value. This is more obvious in another overload:
139
139
140
140
```java
141
141
publicfinal<R>Observable<R> reduce(R initialValue, Func2<R,? super T,R> accumulator)
142
142
```
143
143
144
-
The accumulator returns a different type than the one in the observable. The first argument of the accumulator is the previous partial result of the accumulation process and the second in the next value. To begin the process, an initial value is supplied. We will demonstrate this process by reimplementing `count`
144
+
The accumulator returns a different type than the one in the observable. The first parameter for the accumulator is the previous partial result of the accumulation process and the second is the next value. To begin the process, an initial value is supplied. We will demonstrate the usefulness of this by reimplementing `count`
We start with an accumulator of `0`, as we have counted 0 items. Every time a new item arrives, we return a new accumulator that is increased by one. The last value corresponds to the number of elements in the source sequence.
161
+
160
162
`reduce` can be used to implement the functionality of most of the operators that emit a single value. It can not implement behaviour where a value is emitted before the source completes. So, you can implement `last` using `reduce`, but an implementation of `all` would not behave exactly like the original.
161
163
162
164
### scan
@@ -242,7 +244,7 @@ Output
242
244
[10, 11, 12, 13, 14]
243
245
```
244
246
245
-
The code above has a problem with a formality: `reduce` is meant to be a functional fold and folds are not supposed to work on mutable accumulators. If we were to do this the "right" way, we would have to create a new instance of `ArrayList<Integer>` for every new item, like this:
247
+
The code above has a problem with formality: `reduce` is meant to be a functional fold and such folds are not supposed to work on mutable accumulators. If we were to do this the "right" way, we would have to create a new instance of `ArrayList<Integer>` for every new item, like this:
246
248
247
249
```java
248
250
// Formally correct but very inefficient
@@ -269,7 +271,7 @@ values
269
271
.subscribe(v ->System.out.println(v));
270
272
```
271
273
272
-
Usually, you won't have to collect values manually. Rxjava offers a variety of operators for collecting your sequence into a container. Those aggregators return an observable that will emit the corresponding collection when it is ready.
274
+
Usually, you won't have to collect values manually. Rxjava offers a variety of operators for collecting your sequence into a container. Those aggregators return an observable that will emit the corresponding collection when it is ready, just like what we did here. We will see such aggregators next.
273
275
274
276
#### toList
275
277
@@ -327,7 +329,7 @@ public final <K,V> Observable<java.util.Map<K,V>> toMap(
327
329
328
330
`keySelector` is a function that produces a key from a value. `valueSelector` produces from the emitted value the actual value that will be stored in the map. `mapFactory` creates the collection that will hold the items.
329
331
330
-
Lets start with an example of the simple overload. We want to map people to their age. We start with a data structure
332
+
Lets start with an example of the simplest overload. We want to map people to their age. First, we need a data structure to work on:
If we want to be explicit about the container that will be used or initialise it, we can supply our own:
382
+
If we want to be explicit about the container that will be used or initialise it, we can supply our own container:
381
383
382
384
```java
383
385
values
@@ -388,9 +390,11 @@ values
388
390
.subscribe(newPrintSubscriber("toMap"));
389
391
```
390
392
393
+
The container is provided as a factory function because a new container needs to be created for every new subscription.
394
+
391
395
#### toMultimap
392
396
393
-
When mapping, it is very common that many values share the same key. We call it "grouping" and the datastructure for this is a multimap, a map from keys to collections
397
+
When mapping, it is very common that many values share the same key. The datastructure that maps one key to multiple values is called a multimap and it is a map from keys to collections. This process can also be called "grouping".
The first three overloads are familiar from `toMap`. The fourth allows us to provide not only the `Map` but also the `Collection` that the values will be stored in. The collection can be customised according to the corresponding key:
437
+
The first three overloads are familiar from `toMap`. The fourth allows us to provide not only the `Map` but also the `Collection` that the values will be stored in. The key is provided as a parameter, in case we want to customise the corresponding collection based on the key. In this example we'll just ignore it.
434
438
435
439
```java
436
440
Observable<Person> values =Observable.just(
@@ -450,7 +454,7 @@ values
450
454
451
455
#### Note
452
456
453
-
The operators just presented have actually very limited use. It is tempting for a beginner to collect the data in a collection and process them in the traditional way. That should be avoided not just for didactic purposes, but because this way defeats the advantages of using Rx in the first place.
457
+
The operators just presented have actually limited use. It is tempting for a beginner to collect the data in a collection and process them in the traditional way. That should be avoided not just for didactic purposes, but because this practice defeats the advantages of using Rx in the first place.
454
458
455
459
456
460
### groupBy
@@ -463,9 +467,9 @@ public final <K> Observable<GroupedObservable<K,T>> groupBy(Func1<? super T,? ex
The return value is an observable of observables. Every time a new key is met, a new inner `GroupedObservable` will be emitted. That type is nothing more than a standard observable with a `getKey()` accessor, for getting the group's key. As values come from the source observable, they will be emitted by the observable with the corresponding key.
470
+
The return value is an observable of `GroupedObservable`. Every time a new key is met, a new inner `GroupedObservable` will be emitted. That type is nothing more than a standard observable with a `getKey()` accessor, for getting the group's key. As values come from the source observable, they will be emitted by the observable with the corresponding key.
467
471
468
-
The nested obseravbles may complicate the signature, but they allow the groups to start emitting their items before the source observable has completed.
472
+
The nested observables may complicate the signature, but they offer the advantage of allowing the groups to start emitting their items before the source observable has completed.
469
473
470
474
In the next example, we will take a set of words and, for each starting letter, we will print the last word that occured.
471
475
@@ -511,7 +515,7 @@ t: third
511
515
f: fifth
512
516
```
513
517
514
-
We will formally introduce `map` and `flatMap` in the next chapter.
518
+
`map` and `flatMap` are unknown for now. We will introduce them properly in the next chapter.
515
519
516
520
# Nested observables
517
521
@@ -547,7 +551,7 @@ Output
547
551
2
548
552
```
549
553
550
-
Nesting observables to consume them doesn't make much sense. Towards the end of the pipeline, you'd rather flatten and simply your observables. Nesting is useful when you need to make a non-nested observable be of the same type as a nested observable that you have from elsewhere. Once they are of the same type, you can for example combine them, as we will see in the chapter about [combining sequences](/Part 3 - Taming the sequence/4. Combining sequences.md).
554
+
Nesting observables to consume them doesn't make much sense. Towards the end of the pipeline, you'd rather flatten and simply your observables, rather than nest them. Nesting is useful when you need to make a non-nested observable be of the same type as a nested observable that you have from elsewhere. Once they are of the same type, you can combine them, as we will see in the chapter about [combining sequences](/Part 3 - Taming the sequence/4. Combining sequences.md).
0 commit comments