RxSwift 5 Filter Operator

operator 也是 Rx 的基本元素, 通过它, 可以对 observable 中的事件序列进行操作.� 比如�可以去简单地进行加减. 通过 �operator 的链式表达, 可以实现许多复杂的�逻辑.

本部分首先学习 filter 类型的操作符, 然后是 transform 类型的操作符, 然后再看 combining 类型的操作符. 最后是一些基于时间的操作符, 比如延迟事件, 组合一段时间内的事件等等.

学习完本部分, 就具备了编写一些简单的 RxSwift app 的能力.

学习一个新的技术栈和�建造一栋摩天大楼是一个道理, 首先要打好打牢基础. 在之前的学习中已经学会了Observable, subject, RxSwift 中的内存管理等基础性内容. 下面就在这些的基础上, 再向上学习.

本章中会讲如何通过 RxSwift 的 过滤操作符来操作 next 事件, 这样最终观察者接收到的就是它自己想要接收的数据.

�概述

这里直入主题, 我们先来看再 RxSwift 中如何使用过滤操作符.

过滤型操作符

主要有如下三种:

  • ignoreElements
  • elementAt
  • filter
ignoreElements 操作符

这个操作符的作用是过滤掉所有的 next 事件:

let disbag = DisposeBag()
let sbj = PublishSubject<Int>()
sbj
    // .ignoreElements()
    .subscribe(onNext: {
        print($0)
    }, onError: {
        print($0)
    }, onCompleted: {
        print("complete")
    }, onDisposed: {
        print("dispose")
    })
    .addDisposableTo(disbag)
sbj.onNext(1)
sbj.onNext(2)
sbj.onCompleted()

若不加 ignoreElements, 则输出为:

1
2
complete
dispose

而加了的话:

complete
dispose

可以看到, ignoreElement 操作符的作用的确是将所有的 next 事件过滤掉.

elementAt 操作符

比如只想接收序列中的某个下标位置的元素(从0开始计数), 可以使用 elementAt 操作符.

elementAt 操作符的参数越界的话并不会崩溃, 只会打印错误信息.

上面这两个操作符都是属于过滤操作符中的"忽略"子类中的操作符.

若需要特殊的过滤条件, 则可以使用 filter 操作符.

filter 的原理是利用一个过滤条件块来, 在里面将过滤条件应用到所有的元素上, 这样即可对元素进行过滤. 只有当条件为真的元素才不会被过滤. 即需要保留的元素在应用条件时候的判断结果为真.

exampleOf("过滤", operation: {
    let disposebag = DisposeBag()
    let obsv = Observable.of(1, 2, 3, 4, 56)
    obsv
        .filter({ elem in
            elem > 3
        })
        .subscribe(onNext: {
        print($0)
    }).addDisposableTo(disposebag)
})

上述首先对 observable 的事件序列应用了过滤操作符, 过滤掉所有小于 3 的元素. 输出为:

过滤
4
5
6

跳读操作符(skip)

如果想要自动跳过若干个元素时, 就可以使用 skip 操作符.

比如有三天的天气预报可以被观察, 但只想要今天的, 就可以跳过前两天的预报:

exampleOf("skip", operation: {
    let obsv = Observable.from([1, 2, 3, 48])
    obsv.skip(3).subscribe(onNext: {
        print($0)
    }).addDisposableTo(disBag)
})

输出为:

skip
4
8

实际上 skip 是一个操作符家族, 里面有诸如 skipWhile, 它允许在 skip 的时候同时指定条件. 和 filter 不同的是, skipWhile 是遇到第一个不能 skip 的元素之后, 就不再进行判断了, 后面所有的元素都放行.

并且, skipWhile 的判断条件中, 如果满足条件的会被跳过. 结合起来即: 遇到第一个不满足判断条件的(返回值为 false 的)�元素及其后面的所有元素都会被观察者接收到.

比如下面的代码:

exampleOf("skipWhile", operation: {
    let obsv = Observable.from([1, 2, 3, 48, 1, 2, 3])
    obsv.skipWhile({ elem in
        elem * 2 < 8
    }).subscribe(onNext: {
        print($0)
    }).addDisposableTo(disBag)
})

则其输出为:

skipWhile
4
8
1
2
3

到目前为止, 所有的过滤都是使用一些静态条件. 但如果需要基于其他的 observable 来动态过滤元素呢?

下面就来介绍一些这样的 operator. 首先来看的是 skipUntil, 它会持续在� "源 observable" (就是观察者观察的那个 observable) 中跳过元素, 直到� "触发器 observable"(即另外一个 observable) 发送了 next 事件(而 complete 和 error 均无作用), 此时源 observable 中的后续的所有事件就不会再被跳过了.

比如如下代码:

exampleOf("skipUntil", operation: {
    let sourceObsv = PublishSubject<Strin()
    let triggerObsv = PublishSubject<Strin()
    sourceObsv
    .skipUntil(trigger)
    .subscribe(onNext: {
        print($0)
    }).addDisposableTo(disBag)
    sourceObsv.onNext("A")
    sourceObsv.onNext("B")
    triggerObsv.onNext("something")
    sourceObsv.onNext("C")
})
skipUntil
C

Taking 操作符

Taking 的效果正好和 skip 相反.

比如如下代码:

exampleOf("taking", operation: {
    let obsv = Observable.from([1, 2, 3, 48, 1, 2, 3])
    obsv
        .take(3)
        .subscribe(onNext: {
        print($0)
    }).addDisposableTo(disBag)
})

则输出结果是:

taking
1
2
3

take(_ count:) 操作符的作用是只获取事件序列中的 count 个元素.

相应的, 也有 takeWhile 操作符, 它和 skipWhile 的区别是它一直去取满足条件的, 直到遇到第一个不满足条件的, 而 skipWhile 是满足条件的都被跳过, 直到遇到第一个不满足条件的.

另外还有些情况下需要� takeWhile 满足条件的同时满足�指定的元素下标要求, 此时就可以使用 takeWhileWithIndex:

exampleOf("takeWhileWithIndex", operation: {
    let obsv = Observable.from([5, 4, 8, 12, 3])
    obsv
        .takeWhileWithIndex({ value, idx in
            return value > 3 && idx < 5
        })
        .subscribe(onNext: {
            print($0)
        })
        .addDisposableTo(disBag)
})

输出为:

takeWhileWithIndex
5
4
8

即它去取满足条件的元素, 直到遇到第一个不满足条件的, 而后面的�都不通过.

另外还有 �skipWhileWithIndex, 不同点是它是去跳过. 而和 skipUntil 类似, 有 takeUntil 操作符, 它的作用是一直�允许取 "源 observable" 中的元素, 直到 "触发器 observable" 发送 next 事件.

而它可以用在如下情况:

  • 比如在 RxCocoa 中, 用于将观察者释放. 而无需装入 disposeBag. 但对于大多数情况而言, 放入 disposeBag 都是最佳的选择.

    比如下面的代码:

    someObservable
    .takeUntil(self.rx.deallocated)
    .subscribe(onNext: {
        print($0)
    })
    

    即当自己被释放之前, 都可以获取到源 observable 的事件, 而当 触发器 �observable(这里是 RxCocoa 中的自己) 发送自己被释放的事件时, 就不能再获取源 observable 中的任何 next 事件了.

Distinct 操作符

下面来看一些可以防止多次重复元素通过的操作符.

先来看 distinctUntilChanged 操作符:

1---2---2---1

distinctUntilChanged

1---2------1

它的作用就是过滤掉序列中一次或多次相邻元素是相同的情况.

比如如下代码:

example(of: "Distinct Operator") {
    Observable.of("A", "B", "A", "A", "B", "B")
        .distinctUntilChanged()
        .subscribe(onNext: {
            print($0)
        })
        .addDisposableTo(disposeBag)
}

输出为:

--- Example of: Distinct Operator ---
A
B
A
B

而操作符的判断依据是根据 Equatable 协议, 由于 String 是实现了该协议的, 故可以对 String 类型的序列使用.

但如果是没有实现该协议的元素类型, 要想判断的话, 可以先去实现该协议. 或者是可以使用 distinctUntilChanged(_:) 提供判断条件即可.

example(of: "Distinct with condition") {
    let elem0 = MyElemType(value: 1, name: "elem0")
    let elem1 = MyElemType(value: 2, name: "elem1")
    let elem2 = MyElemType(value: 2, name: "elem2")
    let elem3 = MyElemType(value: 1, name: "elem3")
    Observable.of(elem0, elem1, elem2, elem3)
        .distinctUntilChanged({ (elemA, elemB) -Bool in
            elemA.value == elemB.value
        })
        .subscribe(onNext: {
            print($0)
        })
        .addDisposableTo(disposeBag)
}

输出为:

--- Example of: Distinct with condition ---
MyElemType(value: 1, name: "elem0")
MyElemType(value: 2, name: "elem1")
MyElemType(value: 1, name: "elem3")

可以看到, 通过条件来判断两个元素是否相等操作起来十分方便.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 发现 关注 消息 RxSwift入坑解读-你所需要知道的各种概念 沸沸腾关注 2016.11.27 19:11*字...
    枫叶1234阅读 2,780评论 0 2
  • 注:只包含标准包中的操作符,用于个人学习及备忘参考博客:http://blog.csdn.net/maplejaw...
    小白要超神阅读 2,184评论 2 8
  • RxJava技术分享 京金所—时光 2016.9.22 这里我拿出来给 Android 开发者的 RxJava 详...
    JC_Mobile阅读 5,562评论 3 55
  • 作者: maplejaw本篇只解析标准包中的操作符。对于扩展包,由于使用率较低,如有需求,请读者自行查阅文档。 创...
    maplejaw_阅读 45,600评论 8 93
  • 文/羽商三少 蝉鸣四起,空气轻荡荡,飘来远处花香。 “滴滴滴,六点二十三”手机响起了提示音。 清云姑娘拿着振动着的...
    羽商三少阅读 452评论 5 3