Transforming Operators
toArray Operator
观察者序列(Observables)发送元素(elements)是单个发送的,有时候可能你想配合集合(collections)进行使用,这里有一个非常便利的方式,那就是toArray操作符,它可以转换observable中的所有元素到数组。我们可以看下图理解,toArray将转换observable序列中的元素1,2,3转换到一个数组包含1,2,3这些元素,所以当发送.next事件,接收的元素就是数组:
函数说明
/**
Converts an Observable into another Observable that emits the whole sequence as a single array and then terminates.
转换观察者序列到另一个观察者序列,并把整个序列当中单一数组发送消息并终止。
- returns: An observable sequence containing all the emitted elements as array. 返回一个观察者序列包含所有的元素作为数组
*/
public func toArray() -> RxSwift.Observable<[Self.E]>
具体使用
example(of: "toArray") {
let dispsoseBag = DisposeBag()
Observable.range(start: 1, count: 10)
.toArray()
.subscribe(onNext:{print($0)})
.addDisposableTo(dispsoseBag)
}
/*
--- Example of: toArray ---
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
*/
map Operator
使用具体的闭包(transform function)为观察者序列中的每一个元素执行转换,返回一个新的观察者序列包含转换之后的元素。类似于swift的map。官方解释,对应图表如下:
具体使用
example(of: "map") {
let disposeBag = DisposeBag()
Observable.of(1,2,3)
.map{ $0 * $0 }
.subscribe(onNext: { print($0) })
.addDisposableTo(disposeBag)
}
/*
--- Example of: map ---
1
4
9
*/
mapWithIndex Operator
不仅仅可以获取观察者序列的元素,而且可以获取元素对应的位置,闭包的第一个参数是元素,第二个参数是元素位置,如果我们需要元素位置,那么我们可以通过元素的位置进行操作。对应的图表如下:
具体使用,下面例子对位置大于1的元素值乘2
example(of: "mapWithIndex") {
let disposeBag = DisposeBag()
//1 创建一个Observable<Int>
Observable.of(1,2,3)
//2 对位置大于1的元素进行转换
.mapWithIndex({ integer, index in
index > 1 ? integer * 2 : integer
})
//3 开始订阅,接收事件
.subscribe(onNext: { print($0) })
.addDisposableTo(disposeBag)
}
/*
--- Example of: mapWithIndex ---
1
2
6
*/
flatMap Operator
可以把一个序列转换成一组序列,然后再把这一组序列『拍扁』成一个序列。简而言之:flatMap会保持跟踪观察者序列中每一个的值的改变。flatMap的selector的调用次数和源Element数量一致,但是由于selector的返回值为一个Observable,所以在操作完成后,得到的Elements为源Element数量*selector返回的Observable的Element数量,然后将所有Element放入一个Observable中,类似于[[1, 2], [2, 3], [3, 4]] -> [1, 2, 2, 3, 3, 4]。可以看图大致了解一下:
顶部是观察者序列,包含了3个观察者序列(observable),命名为O1,O2,O3,这些观察者序列是对象类型,都有一个value属性,并拥有初始值,依次为1,2,3,可以当做是Variable进行类比理解,最底部是订阅者。最开始是O1,因为有初始值1,所以经过flatMap函数,会将值乘以10,即第一个元素的值被转换为10,这时候将会把O1观察者序列的元素值10转移到一个新的观察者序列,如图中flatMap下的第一条线,该观察者序列(observable)将发送元素给订阅者,最底部的订阅者会收到元素,值为10。
接下来O1的value属性值改变为4,跟之前一样,同样会执行闭包,得到元素值为40,并转移给跟元素值为10一样的观察者,然后观察者发送元素给订阅者。
接下来是观察者序列O2,拥有初始值2,会执行flatMap函数,结果转换到新的观察者序列,并发送。之后O2的value属性值改变为5,执行flatMap函数,结果转换到新的观察者序列,并发送。
最终是O3观察者序列,初始值为3,同样会执行flatMap函数,结果转换到新的观察者序列,并发送。对于flatMap,不论消息发送的先后顺序,都是可以获取的
函数说明
/*
Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
转换原观察者序列(observable)中的每一个元素到一个新的观察者序列,最终合并所有新的观察者序列到一个单一的观察者序列。
- seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
- parameter selector: A transform function to apply to each element.
转换函数应用于观察者序列的每一个元素
- returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
最终返回的观察者序列中包含的元素值,是经过转换函数即闭包执行一对多的转换输入序列元素之后得到的结果。
*/
public func flatMap<O : ObservableConvertibleType>(_ selector: @escaping (Self.E) throws -> O) -> RxSwift.Observable<O.E>
具体使用,下面例子体现了flatMap访问观察者序列的每一个元素中的观察者序列(Observable)
example(of: "flatMap") {
//1
struct Student {
var score: Variable<Int>
}
let disposeBag = DisposeBag()
//2
let jack = Student(score: Variable(90))
let justin = Student(score: Variable(85))
//3
let student = PublishSubject<Student>()
//4
student.asObservable()
.flatMap {
$0.score.asObservable() //返回值为一个Observable
}
//5
.subscribe(onNext: {print($0)})
.addDisposableTo(disposeBag)
//6
student.onNext(justin)
//7
justin.score.value = 100
//8
student.onNext(jack)
//9
jack.score.value = 80
//10
justin.score.value = 95
}
结果:
--- Example of: flatMap ---
85
100
90
80
95
1: 定义一个学生结构体,拥有score属性,类型为Variable<Int>
2: 实例化两个Student对象,命名为jack和justin
3: 创建一个原观察者序列对象,类型为PublishSubject<Student>
4: 使用flatMap操作符,获取student subject,并获取student的score,因为score是Variable,所以我们可以调用asObservable(),这里我们并没有修改score的值,仅仅是传递。
5: 订阅观察者序列的.next事件,目前为止并没有任何内容打印
6: 发送.next事件,传入justin,将得到打印值85
7: 改变justin的值为100,将会打印100
8: 发送.next事件,传入jack,将得到打印值90
9: 改变jack的值为80,将会打印80
10: 改变justin的值为95,将会打印95
flatMapLatest Operator
flatMapLatest也会新生成一个Observable的队列,但不同的是它不会合并所有的新Observable中的元素,它会switch到最后一个Observable上,先前建立的Observable将不再被监,即先前的Observable被取消监听
函数说明
/**
Projects each element of an observable sequence into a new sequence of observable sequences and then
transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence.
转换原观察者序列(observable)中的每一个元素到一个新的观察者序列,最终合并所有新的观察者序列到一个单一的观察者序列。新的单一观察者序列中值的产生仅仅是由最近的观察者序列生成。
It is a combination of `map` + `switchLatest` operator
相当于map + switchLatest的结合体
*/
public func flatMapLatest<O : ObservableConvertibleType>(_ selector: @escaping (Self.E) throws -> O) -> RxSwift.Observable<O.E>
具体使用,代码仅仅是修改flatMap为flatMapLatest
example(of: "flatMapLatest") {
//1
struct Student {
var score: Variable<Int>
}
let disposeBag = DisposeBag()
//2
let jack = Student(score: Variable(90))
let justin = Student(score: Variable(85))
//3
let student = PublishSubject<Student>()
//4
student.asObservable()
.flatMapLatest {
$0.score.asObservable() //返回值为一个Observable
}
//5
.subscribe(onNext: {print($0)})
.addDisposableTo(disposeBag)
//6
student.onNext(justin)
//7
justin.score.value = 100
//8
student.onNext(jack)
//9
jack.score.value = 80
//10 无效发送
justin.score.value = 95
}
结果:
--- Example of: flatMapLatest ---
85
100
90
80
具体实例:我们常常使用搜索功能,根据用户的输入,请求结果,返回内容显示在列表上,大致如下:
searchBar.rx
.text //获得text
.orEmpty //转换为非nil
.debounce(0.3, scheduler: MainScheduler.instance) //延时0.3秒后获取字符串进行后续操作
.flatMapLatest { string in //1
//根据string进行搜索内容
}.bindTo(tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { index, track, cell in
//配置cell,显示内容
}.addDisposableTo(disposeBag)
对于注释1,为什么使用flatMapLatest?那是因为这里要对观察者序列进行转换,得到新的观察者序列。如:Observable<String>到Observable<[Model]>,Model是我们自己需要的模型数据。但是这里需要注意一个问题,虽然flatMap也可以实现功能,但是flatMap将会执行所有订阅者订阅的事件。如果第一次请求时的返回内容比第二次请求的内容晚些返回,那么内容显示出错,先后顺序不对,所以为了确保每次只请求最新操作,使用flatMapLatest。flatMapLatest将取消之前订阅者的订阅,只处理最新观察者序列的事件。
Scan Operator
Scan类似于Swift原生带的 reduce 方法, 逐层操作, 扫描每一个元素,使用第一个参数值作为初始值,然后执行闭包,闭包使用累加操作符,闭包结果作为下一次的第一个元素值。
记得,这里的 scan 以及 reduce 可不仅仅是只能用来累加,这是一个遍历所有值得过程,所以你可以在这做任何你想做的操作。
public func scan<A>(_ seed: A, accumulator: @escaping (A, Self.E) throws -> A) -> RxSwift.Observable<A>
具体使用
example(of: "scan") {
let disposeBag = DisposeBag()
Observable.of(10, 100, 1000)
.scan(1, accumulator: { aggregateValue, newValue in
aggregateValue + newValue //aggregateValue作为临时变量, 记录上一次返回的值, scan带的参数为aggregateValue的初始值
})
.subscribe(onNext: { print( $0 ) })
.addDisposableTo(disposeBag)
/*
--- Example of: scan ---
11
111
1111
*/
}
开发需求,有时候我们需要使button在选中和未选中状态之间切换,那么我们怎么获取这个状态呢? button.rx.tap方式,我们经常是获取Void值,并没有提供我们任何信息。这里我们可以使用scan,该操作符有2个参数,第一个是初始值,第二个是闭包,闭包包括啦两个值,一个是上一次的值,一个是新值。scan非常有用,提供一个容易类型的初始值,闭包内进行相应的操作!
button.rx.tap.asObservable().scan(false) { lastState, newValue in
return !lastState
}.subscribe(onNext: { value in
print(value)
}).disposed(by: disposeBag)
reduce Operator
给定一个初始值,然后为观察者序列中每一个元素使用闭包累加操作,第一个参数是初始值,第二个参数是序列中的下一个元素值,全部遍历执行完成得到最终一个结果值作为观察者序列返回。注意:和 scan 非常相似,唯一的不同是, reduce 会在序列结束时才发射最终的累加值。就是说,最终只发射一个最终累加值。图形如下:
具体使用
example(of: "reduce") {
let disposeBag = DisposeBag()
Observable.of(10, 100, 1000)
.reduce(1, accumulator: + )
.subscribe(onNext: { print($0) })
.addDisposableTo(disposeBag)
/*
--- Example of: reduce ---
1111
*/
}