preface

In the last article we learned how to create class operators. In this article we will learn how to transform class operators in RxJava. The so-called transformation is to process the object or the whole sequence of events into different events or event sequences. Here’s a look at what the conversion operators are and how they can be used.

Initialize data

Again, use the neighborhood and house example from the first part of the series. Initialize the dummy data first for use when practicing the operator.

// Cell entity
public class Community {
    private String communityName; // Community name
    private List<House> houses; // The house collection
}
// Listing entity
public class House {
    private float size; / / size
    private int floor; / / floor
    private int price; / / the total price
    private String decoration; // The degree of decoration
    private String communityName; // Community name
}Copy the code
private List<Community> communities;

private void initData(a) {
    communities = new ArrayList<>();
    List<House> houses1 = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        if (i % 2= =0) {
            houses1.add(new House(105.6 f, i, 200."Simple decoration"."Oriental Garden"));
        } else {
            houses1.add(new House(144.8 f, i, 520."Luxury decoration"."Oriental Garden"));
        }
    }
    communities.add(new Community("Oriental Garden", houses1));

    List<House> houses2 = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        if (i % 2= =0) {
            houses2.add(new House(88.6 f, i, 166."Medium finish".Madrid Spring));
        } else {
            houses2.add(new House(123.4 f, i, 321."Exquisite decoration".Madrid Spring));
        }
    }
    communities.add(new Community(Madrid Spring, houses2));

    List<House> houses3 = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        if (i % 2= =0) {
            houses3.add(new House(188.7 f, i, 724."Luxury decoration"."Emgrand Homeland."));
        } else {
            houses3.add(new House(56.4 f, i, 101."General decoration"."Emgrand Homeland."));
        }
    }
    communities.add(new Community("Emgrand Homeland.", houses3));
}Copy the code

Conversion operator

Map

The map operator takes an object of the specified type Func1 and applies it to each value emitted by an Observable, converting the emitted value to the desired value. Take a look at the schematic and examples:

// Convert a set of integers to strings
Observable.just(1.2.3.4.5)
        .map(new Func1<Integer, String>() {
            @Override
            public String call(Integer integer) {
                return "This is " + integer;
            }
        })
        .subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                Log.e("rx_test", s); }});// Convert the Community collection to each Community and get its name
Observable.from(communities)
        .map(new Func1<Community, String>() {
            @Override
            public String call(Community community) {
                return community.getCommunityName();
            }
        })
        .subscribe(new Action1<String>() {
            @Override
            public void call(String communityName) {
                Log.e("rx_test"."The community name is:"+ communityName); }});Copy the code

Output result:

This is 1
This is 2
This is 3
This is 4
This is 5District name is: eastern garden district name is: Madrid spring day district name is: Emgrand homeCopy the code

As you can see from the output, the map operator can be used for one-to-one conversions such as data type conversions, concatenation, or traversal of collections. In the first example, Func1

() takes the current type of the transmitted data and the converted data type as the second parameter. The parameter in Action1

is also the data type after transmitting data conversion. Make sure the data types match correctly.

,>

FlatMap

The flatMap operator, which is also used for conversion, differs from the map operator in that flatMap() returns an Observable, which is not sent directly to the Subscriber callback method. This may not be easy to understand, but let’s look at the example of residential and housing. There are three residential areas, and if we want to print out all the housing information in these three residential areas, how can we do it with RxJava? Based on what we learned earlier, we might implement it like this:

Observable.from(communities)
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                for (House house : community.getHouses()) {
                    Log.e("rx_test"."FlatMap: cell name:" + house.getCommunityName() 
                        + ", price: + house.getPrice() + ", floor:+ house.getFloor()); }}});Copy the code

According to this implementation method, we can only obtain the floor of each community. To obtain the housing resources in the community, we need to conduct a layer for loop, which violates the principle of RxJava. So let’s see how flatMap() is implemented:

Observable.from(communities)
        .flatMap(new Func1<Community, Observable<House>>() {
            @Override
            public Observable<House> call(Community community) {
                return Observable.from(community.getHouses());
            }
        })
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                Log.e("rx_test"."FlatMap: cell name:" + house.getCommunityName()
                    + ", price: + house.getPrice() + ", floor:+ house.getFloor()); }});Copy the code

If this code looks a lot more comfortable, take a look at how flatMap() is implemented. First, from() creates an Observable for the community collection after receiving the community collection, and then passes each community in turn to flatMap(). After receiving the community each time, flatMap() takes out the house collection contained therein and creates a house Observable. And activate this house Observable to start emitting events, which are then returned to the Observable of the community collection. Finally, the Observable of the community collection delivers these events to the Subscriber’s callback method for processing. In the whole process, there are two levels of Observable in operation, which is equivalent to paving the initial object Observable of the community set and distributing it through a unified path. This is what flatMap does. Output result:

FlatMap: Community name: Oriental Garden, Price:200, the floor:0FlatMap: Community name: Oriental Garden, Price:520, the floor:1FlatMap: Community name: Oriental Garden, Price:200, the floor:2FlatMap: Community name: Oriental Garden, Price:520, the floor:3FlatMap: Community name: Oriental Garden, Price:200, the floor:4FlatMap: Estate name: Madrid Spring, price:166, the floor:0FlatMap: Estate name: Madrid Spring, price:321, the floor:1FlatMap: Estate name: Madrid Spring, price:166, the floor:2FlatMap: Estate name: Madrid Spring, price:321, the floor:3FlatMap: Estate name: Madrid Spring, price:166, the floor:4FlatMap: Community name: Emgrand Home, Price:724, the floor:0FlatMap: Community name: Emgrand Home, Price:101, the floor:1FlatMap: Community name: Emgrand Home, Price:724, the floor:2FlatMap: Community name: Emgrand Home, Price:101, the floor:3FlatMap: Community name: Emgrand Home, Price:724, the floor:4Copy the code

It can be seen from the output result that all the housing information of the three communities are printed out in sequence. However, a problem of flatMap() is that the output data may be staggered when the amount of data is too large. Official schematic diagram:

ConcatMap

ConcatMap operator, similar to the flatMap() function. The difference is that concatMap() uses concatenation rather than merge, so the data emitted by concatMap() is strictly in order, which eliminates the possibility of data interlacing in flatMap(). Schematic diagram:

FlatMapIterable

The flatMapIterable operator is similar to flatMap() except that flatMapIterable converts multiple Observables using Iterable as the source data.

Observable.from(communities)
        .flatMapIterable(new Func1<Community, Iterable<House>>() {
            @Override
            public Iterable<House> call(Community community) {
                return community.getHouses();
            }
        }).subscribe(new Action1<House>() {
    @Override
    public void call(House house) {
        Log.e("rx_test"."FlatMap: cell name:" + house.getCommunityName()
                    + ", price: + house.getPrice() + ", floor:+ house.getFloor()); }});Copy the code

SwitchMap

The switchMap conversion operator, also similar to flatMap(), unsubscribes each time the source Observable emits a new data item (Observable), stops monitoring the previous data item producing Observable, and starts monitoring the current emitted one.

Observable.from(communities)
        .switchMap(new Func1<Community, Observable<House>>() {
            @Override
            public Observable<House> call(Community community) {
                return Observable.from(community.getHouses());
            }
        })
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                Log.e("rx_test"."FlatMap: cell name:" + house.getCommunityName()
                    + ", price: + house.getPrice() + ", floor:+ house.getFloor()); }});Copy the code

As the previous example, when the amount of data is large, the small house Observable generated by the first community is transmitting data at a certain moment, and the small house Observable generated by the second community is activated, so the subscription of the small Observable in the first community will be cancelled, and the data that has not been transmitted will not be transmitted. The second cell Observable starts emitting data, and so on. Schematic diagram:

Scan

The scan operator applies a function to a sequence of data and emits the result of the function as the first argument to the next data application function.

For example, output 1, then send 1+2=3 as the next data, 3+3=6 as the next data, and so on.
Observable.just(1.2.3.4.5)
        .scan(new Func2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer integer, Integer integer2) {
                return integer + integer2;
            }
        })
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {
                Log.e("rx_test"."scan:"+ integer); }});Copy the code

Output result:

Scan:1Scan:3Scan:6Scan:10Scan:15Copy the code

Schematic diagram:

GroupBy

The groupBy operator divides the data emitted by the original Observable into small observables according to the key, and then these small Observables emit the data contained in them respectively. Generally speaking, the data is classified according to a certain field and then transmitted. Consider an example: multiple housing inventory data in several neighborhoods, which now needs to be sorted and output by neighborhood name.

List<House> houseList = new ArrayList<>();
houseList.add(new House(105.6 f.1.200."Simple decoration"."Oriental Garden"));
houseList.add(new House(144.8 f.3.300."Luxury decoration".Madrid Spring));
houseList.add(new House(88.6 f.2.170."Simple decoration"."Oriental Garden"));
houseList.add(new House(123.4 f.1.250."Simple decoration"."Emgrand Homeland."));
houseList.add(new House(144.8 f.6.350."Luxury decoration".Madrid Spring));
houseList.add(new House(105.6 f.4.210."General decoration"."Oriental Garden"));
houseList.add(new House(188.7 f.3.400."Exquisite decoration"."Emgrand Homeland."));
houseList.add(new House(88.6 f.2.180."General decoration"."Oriental Garden"));
// Divide by cell name
Observable<GroupedObservable<String, House>> groupByCommunityNameObservable = Observable
        .from(houseList)
        .groupBy(new Func1<House, String>() {
            @Override
            public String call(House house) {
                // Provide the key of the classification rule
                returnhouse.getCommunityName(); }}); Observable.concat(groupByCommunityNameObservable)// The concat compositional operator combines and sends multiple Observables in order. More on this later
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                Log.e("rx_test"."groupBy:" + "Community:" + house.getCommunityName() + ", price:+ house.getPrice()); }});Copy the code

To create a new observables: groupByCommunityNameObservable, it will be sent with a GroupedObservable sequence (i.e., refers to the type of data item send GroupedObservable). GroupedObservable is a special Observable that is based on a group key, in this case the cell name. Output result:

GroupBy: Oriental Garden, Price:200GroupBy: Oriental Garden, Price:170GroupBy: Oriental Garden, Price:210GroupBy: Oriental Garden, Price:180GroupBy: Madrid Spring, price:300GroupBy: Madrid Spring, price:350GroupBy: Emgrand Home, price:250GroupBy: Emgrand Home, price:400Copy the code

Schematic diagram:

conclusion

The next article will take a look at the filter operators and how to use them. If you have any doubts or suggestions, you can also put forward them in the project Issues of RxJavaDemo on Github. I will reply in time. Attached is the address of RxJavaDemo: RxJavaDemo