RxSwift FilterOperators チートシート

##FilterOperators

RxSwiftでコードを書く必要があり、学習中です。
参考文献を元に学んだ内容をすぐ振り返れるようにまとめていきます。
今回はFilter Operatersについてまとめました。
完全に自分の解釈でコメントを載せていますが、解釈に誤りがありましたら、コメント欄より教えてください。

###elementAt()
指定したインデックスの場合のみ、インベントは発火

```Swift
import Foundation
import RxSwift
  // 1
  let strikes = PublishSubject<String>()
  let disposeBag = DisposeBag()

  //  2
  strikes
    .elementAt(2)
    .subscribe(onNext: { _ in
      print("You're out!")
    })
    .disposed(by: disposeBag)
    
    strikes.onNext("X")
    strikes.onNext("X")
    strikes.onNext("X")
```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.26.48 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/add2b9bb-e3aa-c078-151d-84affe7243a5.png)

###filter()
この場合、2で割り切れる値のみ表示する。条件に当てはまる場合のみ、イベントを発火。

```Swift
  let disposeBag = DisposeBag()
  // 1
  Observable.of(1, 2, 3, 4, 5, 6)
    // 2
    .filter { $0.isMultiple(of: 2) }
    // 3
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)

```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.26.21 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/ee0e8a33-f241-4dca-da52-e258d6c5fbb1.png)


###Skip()
先頭か指定された数まで、イベント発火をスキップする。

```Swift
  let disposeBag = DisposeBag()

  // 1
  Observable.of("A", "B", "C", "D", "E", "F")
    // 2
    .skip(3)
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)

```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.25.57 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/c564d4d9-f32f-5da6-4c0c-e797381c9f5b.png)


###SkipWhile()
ある条件が成立するまで、イベントを通さない。一度通ったあとは、全部通す。

```Swift
  // 1
  Observable.of(2, 2, 3, 4, 4)
    // 2
    .skipWhile { $0.isMultiple(of: 2) }
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.25.17 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/b476ecee-348e-a8c1-7044-dfc815b94c01.png)

###SkipUntil()
動的にイベントを発火させる。トリガーが起動された後のイベントを全て発火する。

```Swift
  let disposeBag = DisposeBag()

  // 1
  let subject = PublishSubject<String>()
  let trigger = PublishSubject<String>()

  // 2
  subject
    .skipUntil(trigger)
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
    
    subject.onNext("A")
    subject.onNext("B")
    trigger.onNext("X")
    subject.onNext("C")
    subject.onNext("D")

```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.24.51 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/338598a3-a35f-cdd6-51b5-c0c628548a4c.png)

###take()
Skipオべレーターの逆、指定した数までイベントを発火、それ以降はイベントを止める。

```Swift
  let disposeBag = DisposeBag()

  // 1
  Observable.of(1, 2, 3, 4, 5, 6)
    // 2
    .take(3)
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)

```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.24.20 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/701543e9-6bc9-cf13-c065-b177319238b5.png)

###takeWhile()
条件が満たされる間は、イベントを発火

```Swift
  let disposeBag = DisposeBag()

  // 1
  Observable.of(2, 2, 4, 4, 6, 6)
    // 2
    .enumerated()
    // 3
    .takeWhile { index, integer in
      // 4
      integer.isMultiple(of: 2) && index < 3
    }
    // 5
    .map(\.element)
    // 6
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)

```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.23.39 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/207aa683-747f-e7a1-175e-26c37e1e1b3f.png)

###takeUntil()
条件が満たされるまでイベントを発火。最期のイベントを発火するかどうか選択できる。

```Swift
  // 1
  Observable.of(1, 2, 3, 4, 5)
    // 2
    .takeUntil(.exclusive) { $0.isMultiple(of: 4) }
    .subscribe(onNext: {
      print($0)
    })
  .disposed(by: disposeBag)

```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.23.05 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/02bb5ff3-7e52-130c-93d3-a29b260d3bef.png)

###takeUntil() Trigger
トリガーが軌道するまで、イベントを発火。それ以降は発火しない。

```Swift
  let disposeBag = DisposeBag()

  // 1
  let subject = PublishSubject<String>()
  let trigger = PublishSubject<String>()

  // 2
  subject
    .takeUntil(trigger)
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)

  // 3
  subject.onNext("1")
  subject.onNext("2")
  trigger.onNext("X")
  subject.onNext("3")
```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.21.41 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/d594440e-c38b-fb45-044d-6825f4312035.png)

###distinctUntilChanged()
エレメントが重複する場合、一度目のイベントのみを発火、それ以降はエレメントが変わるまでイベントを発火しない。


```Swift
  // 1
  Observable.of("A", "A", "B", "B", "A")
    // 2
    .distinctUntilChanged()
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)

```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.20.28 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/59394839-4610-b537-f237-3e1bc5d03eb4.png)

###distinctUntilChanged() 条件付き
後の数字の文字列に同じ要素が含まれないか確認、含まれる場合はスキップ、含まれない場合はイベントを発火！

```Swift

  // 1
  Observable.of("A", "A", "B", "B", "A")
    // 2
    .distinctUntilChanged()
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
}

  let disposeBag = DisposeBag()
  
  // 1
  let formatter = NumberFormatter()
  formatter.numberStyle = .spellOut

  // 2: フォーマッターで変換しなくていいように、はじめからNSNumberに！！
  Observable<NSNumber>.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 where bWords.contains(aWord) {
        print(aWord)
        containsMatch = true
        break
      }

      return containsMatch
    }
    // 6
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)

```
**コンパイル結果**
![Screen Shot 2020-11-23 at 12.19.58 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/1c272506-4eab-e199-f3b9-81f764004db8.png)

##Filter応用

```Swift
import Foundation
import RxSwift

  let disposeBag = DisposeBag()
  
  let contacts = [
    "603-555-1212": "Florent",
    "212-555-1212": "Shai",
    "408-555-1212": "Marin",
    "617-555-1212": "Scott"
  ]
  
  func phoneNumber(from inputs: [Int]) -> String {
    var phone = inputs.map(String.init).joined()
    
    phone.insert("-", at: phone.index(
      phone.startIndex,
      offsetBy: 3)
    )
    
    phone.insert("-", at: phone.index(
      phone.startIndex,
      offsetBy: 7)
    )
    
    return phone
  }
  
  let input = PublishSubject<Int>()
  
  input
    .skipWhile {return $0 == 0}
    .filter {return  $0 < 10 }
    .take(10)
    .toArray()
    .subscribe(onSuccess: {
      let phone = phoneNumber(from: $0)
      if let contact = contacts[phone] {
        print("Dialing \(contact) (\(phone))...")
      } else {
        print("Contact not found")
      }
    })
    .disposed(by: disposeBag)
  
    //0 is rejected
  input.onNext(0)
    //Only figure less than 10 can be passed 
  input.onNext(603)
  
  input.onNext(6)
  input.onNext(0)
  input.onNext(3)
  
  "5551212".forEach {
    if let number = (Int("\($0)")) {
      input.onNext(number)
    }
  }
  
  input.onNext(9)
```
**コンパイル結果**
![Screen Shot 2020-11-23 at 1.16.36 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535198/f255bbc8-18f1-dd29-214b-b2c085defb02.png)

##参考文献
RxSwift: Reactive Programming with Swift
Filtering Operators Written by Scott Gardner
詳細が気になる方はこちらをご参照ください！
https://www.raywenderlich.com/13285844-rxswift-reactive-programming-with-swift-update-now-available