章节4:Filtering Operators

学习一门新技术有点像建造摩天大楼。在你摸到天空之前必须建立坚实的基础。现在已经建立了一个坚实的RxSwift基础,是时候一层一层的学习基础知识和技巧了。

本章将教你RxSwift的过滤操作符,您可以使用条件约束.next 事件,所以用户只接收要处理的elements。如果你使用过filter(_:)方法,你已经成功一半了,如果没有也不要担心,在本章结束你可能会成为一个专家。

Ignoring operators

闲话少说,直接看RxSwift中有用的filtering操作符,从ignoreElements开始。如下面图表描述的一样,ignoreElements会忽视.next事件。然而,它允许stop事件通过,即.completed或.error事件。

章节4:Filtering Operators_第1张图片
DE50A665-FF37-4F16-9BAE-11AABD471525.png

示例:

example(of: "ignoreElements") {
  // 1
  let strikes = PublishSubject()
  let disposeBag = DisposeBag()
  // 2
  strikes
    .ignoreElements()
    .subscribe { _ in
      print("You're out!")
    }
    .addDisposableTo(disposeBag)
}

他们做了什么:
1.创建一个变量strikes subject
2.订阅strikes的所有事件,忽略所有的.next事件

当一个observable被.completed 或者 .error事件终止时你想得到通知ignoreElements就会很有用,

strikes.onNext("X")
strikes.onNext("X")
strikes.onNext("X")

尽管发送了很多次,但是没有打印任何东西,因为你忽略了.next事件。需要添加.completed事件来通知订阅者。如下:

 strikes.onCompleted()

现在订阅者接收到.completed事件,打印出:

--- Example of: ignoreElements ---
You're out!

有些时候你只想处理observable发送的第n(按顺序)个元素。你可以使用elementAt,用下标获取你想接收的element,然后忽略其他。下图中,只有下标为1的element会通过,所以只会通过第二个element。

章节4:Filtering Operators_第2张图片
9AB19063-5359-45F2-84BA-A69552B233CB.png
example(of: "elementAt") {
  // 1
  let strikes = PublishSubject()
  let disposeBag = DisposeBag()
// 2
  strikes
    .elementAt(2)
    .subscribe(onNext: { _ in
      print("You're out!")
    })
    .addDisposableTo(disposeBag)
}

一步一步演示:
1.创建一个subject
2.订阅.next事件,忽略所有.next事件,除开第三个(下标为2)。

现在你向subject上添加新的strikes,你就会知道是触发的哪一个,代码如下:

strikes.onNext("X")
strikes.onNext("X")
strikes.onNext("X")

打印:

--- Example of: elementAt ---
You're out!

ignoreElements和elementAt都可以过滤observable发送的elements。filter就像是一个predicate闭包,它去匹配每个element,只有当匹配通过时才允许通过。

查看一下图表,只有1和2通过,因为filter的predicate只允许小于3的elements通过。

章节4:Filtering Operators_第3张图片
8CBD5B4A-7994-4DAE-8793-74950F71CD68.png

示例:

example(of: "filter") {
  let disposeBag = DisposeBag()
    // 1
  Observable.of(1, 2, 3, 4, 5, 6)
    // 2
    .filter { integer in
      integer % 2 == 0
    }
    // 3
    .subscribe(onNext: {
      print($0)
})
    .addDisposableTo(disposeBag)
}

1.创建了一个定于integers的observable
2.用filter操作符匹配约束条件来防止奇数通过。filter携带一个Bool的predicate,允许为true的元素通过和防止false的元素通过
3.订阅和打印出通过filter predicate的elements

--- Example of: filter ---
2
4
6

Skipping operators

你可能会需要跳过某一个elements。就像天气预报一样,也许你不需要每小时的预测数据,因为你一直在房间里。skip操作符允许从第一个到第几个忽略。下表展示了忽略前2个elements。

章节4:Filtering Operators_第4张图片
21563104-759F-4FAD-8784-09F4F676EA48.png

示例:

example(of: "skip") {
  let disposeBag = DisposeBag()
    // 1
  Observable.of("A", "B", "C", "D", "E", "F")
    // 2
    .skip(3)
    .subscribe(onNext: {
print($0) })
    .addDisposableTo(disposeBag)
}

1.创建一个字母型的observable
2.用skip跳过前3个elements和订阅.next事件

在忽略前3个elements之后只剩下D,E:

--- Example of: skip ---
D
E
F

skip操作符有几种。像filter,skipWhile允许你用一个predicate来觉得什么需要被skipped。然而,不像filter从订阅就开始filter,skipWhile会直接跳过,直到遇到被通过的元素,从那时起就会都会通过,跳过不在起作用了。

skipWhile返回true会使element被跳过,返回false会通过。和filter相反。下图中,1直接被跳过,2允许被通过,3会被通过因为skipWhile不在起作用了。

章节4:Filtering Operators_第5张图片
B48CBCD0-112F-44FC-B9CF-EBA3ACF6139C.png

示例:

example(of: "skipWhile") {
  let disposeBag = DisposeBag()
    // 1
  Observable.of(2, 2, 3, 4, 4)
    // 2
    .skipWhile { integer in
      integer % 2 == 0
    }
    .subscribe(onNext: {
      print($0)
})
    .addDisposableTo(disposeBag)
}

1.创建一个integers的observable
2.用skipWhile来过滤元素直到遇到基数

skip只是跳过第一个element通过之前的所有elements,然后就允许所有的elements通过:

--- Example of: skipWhile ---
3
4
4

到目前为止,过滤的都是基于一些静态条件。如果你想基于一些observable来动态过滤elements呢?下面这两个操作符可以做到。第一个是skipUntil,它将会保持跳过observable上的elements直到触发observable发送事件。下图中,skipUntil忽略了observable上的elements直到第二行的observable触发.next事件。然后从此时开始就停止跳过和允许所有elements通过。

章节4:Filtering Operators_第6张图片
55D36DB2-0AB6-4C78-863D-D2189AFFD0DC.png

示例:

example(of: "skipUntil") {
  let disposeBag = DisposeBag()
// 1
  let subject = PublishSubject()
  let trigger = PublishSubject()
// 2
  subject
    .skipUntil(trigger)
    .subscribe(onNext: {
print($0) })
    .addDisposableTo(disposeBag)
}

1.创建subject,创建另一个subject(trigger)来触发第一个subject
2.用skipUntil来跳过subject。直到trigger触发,skipUnti才停止跳过

添加下面两个.next事件:

subject.onNext("A")
subject.onNext("B")

不会有任何打印,因为都被跳过了,现在添加一个新的.next事件到trigger上:

trigger.onNext("X")

这使skipUntil停止跳过。从现在起,所有elements都将被通过:

subject.onNext("C")

打印结果:

--- Example of: skipUntil ---
C

Taking operators

Taking和skipping相反。当你想得到某个elements,RxSwift已经提供了。第一个taking操作符就是take,如下图所示,将会取前两个elements。

章节4:Filtering Operators_第7张图片
40BF8327-6BDD-4C92-B376-3321FB980CAC.png

示例:

example(of: "take") {
  let disposeBag = DisposeBag()
    // 1
  Observable.of(1, 2, 3, 4, 5, 6)
    // 2
    .take(3)
    .subscribe(onNext: {
print($0) })
    .addDisposableTo(disposeBag)
}

1.创建integers型的observable
2.用take取前3个elements

打印:

--- Example of: take ---
1
2
3

takeWhile操作符和skipWhile相似,有时你想关联element的下标,这有另一中takeWhile类型的是:takeWhileWithIndex。当predicate为真时会通过,也会通过你关联下标的元素。

下图中,要求值大于1和下标大于1,所以2能通过第一个要求,但是下标是1不能通过第二个要求。

章节4:Filtering Operators_第8张图片
96A17815-8E86-4B35-8D50-9AD213000E96.png

示例:

example(of: "takeWhileWithIndex") {
  let disposeBag = DisposeBag()
     // 1
  Observable.of(2, 2, 4, 4, 6, 6)
     // 2
    .takeWhileWithIndex { integer, index in
     // 3
      integer % 2 == 0 && index < 3
    }
     // 4
    .subscribe(onNext: {
      print($0)
})
    .addDisposableTo(disposeBag)
}

1.创建一个integers型的observable。
2.用takeWhileWithInde操作符,创建一个接受元素和元素下标的闭包
3.只能通过偶数和下标小鱼3的元素
4.订阅和打印

结果就是你直接受到了偶数和下标小于3的元素.

--- Example of: takeWhileWithIndex ---
2
2
4

这里也有像这样的skipWhileWithIndex操作符,不像skipping,也不像taking。就像skipUntil,也有一种takeUntil操作符,如图所示,从observable上获取元素直到trigger发送element。

章节4:Filtering Operators_第9张图片
F0850F06-7CBD-499A-93C0-B6CCB8D73D35.png

示例:

example(of: "takeUntil") {
  let disposeBag = DisposeBag()
// 1
  let subject = PublishSubject()
  let trigger = PublishSubject()
// 2
  subject
    .takeUntil(trigger)
    .subscribe(onNext: {
        print($0) })
    .addDisposableTo(disposeBag)
// 3
  subject.onNext("1")
  subject.onNext("2")
}

1.创建subjec和trigger。
2.用takeUntil获取元素直到trigger触发。
3.向subject中添加两个element

打印:

--- Example of: takeUntil ---
1
2

现在向trigger添加element,向element中添加另一个element:

trigger.onNext("X")
subject.onNext("3")

X终止了taking,所以3不被允许通过,不会打印出来。

和RxCocoa库的api用法一致,takeUntil也会用来dispose订阅,代替把它添加到dispose bag.

在observables用if-then-but-what很容易导致内存泄漏,或者将你的代码复杂化。单纯的将subscription添加到dispose bag是一个好的方式。

为了演示完整,你怎样使用RxCocoa中的takeUntil(不要将这段代码加入到示例中,因为不会编译通过):

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

上面一段代码中,trigger会造成takeUntil停止taking和销毁自己,典型的就是在 view controller 或 view model中。

Distinct operators

如图所示,distinctUntilChanged可以防止连续重复的items通过,所以只有第一个2通过。

章节4:Filtering Operators_第10张图片
D1A00FE7-A564-4339-B8BF-8099EB130406.png

示例:

example(of: "distinctUntilChanged") {
  let disposeBag = DisposeBag()
// 1
Observable.of("A", "A", "B", "B", "A")
  // 2
  .distinctUntilChanged()
  .subscribe(onNext: {
      print($0) })
  .addDisposableTo(disposeBag)
}

1.创建字符串类型的observable
2.用distinctUntilChange来防止连续重复的通过。

distinctUntilChanged仅仅用来防止连续重复的。所以第二个A被阻止了因为和第一个相同,但是最后一个A可以通过,因为它在不同的B之后,打印结果包括第一个A,第一个B和最后一个A。

--- Example of: distinctUntilChanged ---
A
B
A

这些都是字符串,遵循Equatable。所以,这些元素比较遵循Equatable的实现。然而,您可以distinctUntilChanged(_:)提供自己的自定义比较逻辑,未命名的参数是一个比较器。

下图中,基于对象的value属性比较。

章节4:Filtering Operators_第11张图片
524E4430-952A-4E52-8A56-0A007FC29EC4.png

用distinctUntilChanged(_:)添加一个简短的案例:

example(of: "distinctUntilChanged(_:)") {
  let disposeBag = DisposeBag()
  // 1
  let formatter = NumberFormatter()
  formatter.numberStyle = .spellOut
  // 2
  Observable.of(10, 110, 20, 200, 210, 310)
    // 3
    .distinctUntilChanged { a, b in
      // 4
      guard let aWords = formatter.string(from:a)?.components(separatedBy: " "),
      let bWords = formatter.string(from: b)?.components(separatedBy: "")
            else {
                  return false
                  }
      var containsMatch = false
      // 5
      for aWord in aWords {
        for bWord in bWords {
          if aWord == bWord {
            containsMatch = true
            break
        }
    }
}
      return containsMatch
    }
     // 4
    .subscribe(onNext: {
      print($0)
})
    .addDisposableTo(disposeBag)
}

--- Example of: distinctUntilChanged(_:) ---
10
20
200

你可能感兴趣的:(章节4:Filtering Operators)