Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[RxSwift] 5.Filtering Operators 본문

RxSwift/RxSwift 기본

[RxSwift] 5.Filtering Operators

jake-kim 2020. 5. 26. 20:52

구독 요청시, filtering하는 연산자를 넣어서

이벤트(onNext)들을 filter하여 선택적인 emit을 하기위한 연산자

1. Ignoring operators

1)  ignoreElements()

- next이벤트 금지 필터 (completed, error는 사용가능)

/// ignoreElements
example(of: "ignoreElements") {
  // 1
  let strikes = PublishSubject<String>()

  let disposeBag = DisposeBag()

  // 2
  strikes
    .ignoreElements()
    .subscribe { _ in
      print("You're out!")
    }
    .disposed(by: disposeBag)

    strikes.onNext("X") // print : none of print
    strikes.onCompleted() // print : You're out!
}

 

2) elementAt()

- 입력한 인덱스 번호만 이벤트 수신

example(of: "elementAt") {
    
    let strikes = PublishSubject<String>()
    
    let disposeBag = DisposeBag()
    
    strikes
        .elementAt(2) // 인덱스로 2번(3번째 추가한 아이템 선택)
        .subscribe(onNext: { item in
            print(item)
        })
        .disposed(by: disposeBag)
    
    strikes.onNext("first")
    strikes.onNext("second")
    strikes.onNext("third") // print : third
}

 

3) filter

emit할 값에 대한 조건을 명시적으로 설정

example(of: "filter") {
  let disposeBag = DisposeBag()

  Observable.of(1, 2, 3, 4, 5, 6)

    .filter { $0 % 2 == 0 }

    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
}

/* prints
 2
 4
 6
 */

2. Skipping operators

1) skip(_:)

입력한 갯수만큼 이벤트를 skip

example(of: "skip") {
  let disposeBag = DisposeBag()

  Observable.of("A", "B", "C", "D", "E", "F")

    .skip(3)
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
}

/* prints
 D
 E
 F
 */

2) skipWhile

- 조건에 만족하는 것을 skip, 조건이 맞는 원소가 발견되고 그 값이 연속적이면 계속 skip, 단 1회성(휘발성)

 - 아래 예제에서 최초의 값이 2로 나누어 떨어지지 않으면, 필터링 하지 않고 모두 emit

* 4값과 끝에 2값은 skip안되는 것을 확인

example(of: "skipWhile") {
    let disposeBag = DisposeBag()
    
    Observable.of(2, 2, 2, 3, 4, 4, 2)
        
        .skipWhile { $0 % 2 == 0 }
        .subscribe(onNext: {
            print($0)
        })
        .disposed(by: disposeBag)
}

/* prints
 3
 4
 4
 2
 */

3) skipUntil

또 다른 subject를 두어서, 이 이벤트가 발생하기 전까지 event들을 skip

example(of: "skipUntil") {
  let disposeBag = DisposeBag()

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

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

3. Taking operators

1) take(_:)

입력한 갯수만큼만 이벤트 허용

example(of: "take") {
  let disposeBag = DisposeBag()

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

/* prints
  1
  2
  3
*/

 

2) takeWhile(_:)

특정 조건까지 emit

- enumerated() : index와 배열의 원소 값을 얻을 수 있게끔 설정

- map{ $0.element } : takeWhile()후의 값은 이미 배열을 순회한 값(enumerated를 사용했으므로 for문처럼 접근한 것)

  즉, enumerated()로 인해서 takeWhile()의 결과는 현재 하나의 튜플형태

  튜플에 한 원소에 하나씩 접근하기 위해서 map을 사용 하는 것

example(of: "takeWhile") {
  let disposeBag = DisposeBag()

  Observable.of(2, 2, 4, 4, 6, 6, 2)

    .enumerated()

    .takeWhile { index, integer in

      integer % 2 == 0 && index < 3
    }
	
    // 원소에 접근하는 중요한 작업
    .map { $0.element }

    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
}

/* prints
 2
 2
 4
 */

 

3) takeLast(_:)

완료가 되면 count만큼 이전의 이벤트 발생

takeLast

4. Distinct operators

1) distinctUntilChanged()

중복값 방지

example(of: "distinctUntilChanged") {
  let disposeBag = DisposeBag()

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

/* prints
 A
 B
 A
 */

 

2) distinctUntilChanged의 클로저 사용

- distinctUntilChanged

- 정수 발음을 문자열로 바꾼다음에 한 문자열이 다른에 문자열에 속해있다면 true를 반환하게끔 설정(true : emit하지 말라는 의미)

(단 바뀌는 지점 emit)

example(of: "distinctUntilChanged(_:)") {
    let disposeBag = DisposeBag()
    
    // formatter.string(from: 110) -> Optional("one hundred ten")
    let formatter = NumberFormatter()
    formatter.numberStyle = .spellOut
    
    Observable<NSNumber>.of(10, 110, 20, 200, 210, 310)
        
        .distinctUntilChanged { a, b in
            print("a=\(formatter.string(from: a)!), b=\(formatter.string(from: b)!)")
            
            guard let aWords = formatter.string(from: a)?.components(separatedBy: " "),
                let bWords = formatter.string(from: b)?.components(separatedBy: " ")
                else {
                    return false
            }
            
            var containsMatch = false
            
            for aWord in aWords where bWords.contains(aWord) {
                containsMatch = true
                break
            }
            
            return containsMatch
    }
        
    .subscribe(onNext: {
        print($0)
    })
        .disposed(by: disposeBag)
}
/* prints
 10
 a=ten, b=one hundred ten
 a=ten, b=twenty
 20
 a=twenty, b=two hundred
 200
 a=two hundred, b=two hundred ten
 a=two hundred, b=three hundred ten
 */

5. 시간과 관련된 연산자

1) Debounce

- 주어진 시간 후 이벤트 emit

- x초 후에 가장 마지막 이벤트 emit

2) Throttle

- 이벤트 발생 시, 해당 이벤트 emit 후 이후 주어진 시간 동안의 다른 이벤트 무시하다가 해당 시간이 흐르면 가장 최신의 이벤트를 emit

* 사용 : 뷰 컨트롤러를 present할 때 중복으로 tap하는 것을 방지

 

*참조 : RxSwift: Reactive Programming with Swift, by raywenderlich team's book

 

'RxSwift > RxSwift 기본' 카테고리의 다른 글

[RxSwift] 7.Transforming Operators  (0) 2020.06.01
[RxSwift] 6.Filtering Operators 실습  (0) 2020.05.29
[RxSwift] 4. Observables and Subjects 실전 적용  (0) 2020.05.25
[RxSwift] 3. Subjects  (0) 2020.05.22
[RxSwift] 2. Observables  (0) 2020.05.21
Comments