본문 바로가기
iOS/RxSwift

Ch5. Filtering Operators

by 헤콩 2023. 2. 24.
반응형

본 게시물은 Florent Pillet, Junior Bontognali, Marin Todorov, Scott Gardner - RxSwift.  Reactive Programming with Swift (2017, Razeware LLC) 책과 ReactiveX 사이트를 기반으로 작성되었습니다.

 

RxSwift를 공부하는 데에 이 책을 읽으면 좋겠다고 생각하였고, 단지 읽기만 하는 것보다 한국어로 직접 정리해놓으면 더 기억하기 좋을 것 같아 게시물을 작성하게 되었습니다.

이번 게시물에서는 마블 다이어그램과, 마블 다이어그램에 해당하는 메서드 예시를 중점으로 정리하였습니다.

 


챕터 이름에서 알 수 있듯이, 이번 게시물에서는 원하는 값을 얻기위해 필터링을 하는 Filtering Operator들을 알아보겠습니다.

 

👉ignoreElements, elementAt, filter, skip, skipWhile, skipUntil, take, takeWhile, enumerated, takeUntil, distinctUntilChanged

 

A. Ignoring Operators

I. ignoreElements( )

ignoreElements는 next 이벤트를 무시하고 completed나 error 이벤트와 같은 종료 이벤트만 허용합니다. 아래 예시를 보면 next 이벤트에서는 반응하지 않다가 completed 이벤트에 반응하는 것을 알 수 있습니다.

let publishSubject = PublishSubject<String>()
let disposeBag = DisposeBag()

publishSubject
    .ignoreElements()
    .subscribe { _ in
        print("hello, world!")
    }
    .addDisposableTo(disposeBag)
    
publishSubject.onNext("X")
publishSubject.onNext("X")
publishSubject.onNext("X")

publishSubject.onCompleted()

 

[ 출력 ]

hello, world!

 

 

II. elementAt(n)

우리는 가끔 특정한 순서의 element, 즉 n번째 방출되는 element만 처리하고 싶은 경우가 있을 수 있습니다. 이 때 elementAt(n)을 사용하면, 해당 index에 해당하는 element만을 방출하고 나머지는 무시합니다.

let publishSubject = PublishSubject<String>()
let disposeBag = DisposeBag()

publishSubject
    .elementAt(1)
    .subscribe(
        onNext: { _ in
            print("hello, world!")
        }
    )
    .addDisposableTo(disposeBag)
    
publishSubject.onNext("X")
publishSubject.onNext("X")
publishSubject.onNext("X")

[ 출력 ]

hello, world!

 

 

III. filter(조건)

앞서 살펴보았던 ignoreElements와 elementAt은 방출되는 element를 필터링합니다. 만약 우리가 필터링 하고싶은 요구사항이 한 가지 이상일 때filter를 사용하면 됩니다. 아래 예시를 살펴보면, 값이 3 미만인 element들만 방출되는 것을 알 수 있습니다.

let disposeBag = DisposeBag()

Observable.of(1,2,3,4,5,6)
    .filter { value in
        value < 3
    }
    . subscribe(
        onNext: {
            print($0)
        }
    )
    .addDisposableTo(disposeBag)

 

[ 출력 ]

1

2

 

 

B. Skipping Operators

I. skip(n)

우리는 가끔 몇 개의 element들을 skip하고 싶을 때가 있습니다. 예를 들어 날씨 예보의 경우, 특정 시간 이후의 시간별 데이터를 받고 싶지 않을 수 있습니다. skip 연산자는 우리가 첫 element부터 우리가 파라미터로 설정한 element까지 skip할 수 있도록 합니다.

let disposeBag = DisposeBag()

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

 

[ 출력 ]

D

E

F

 

 

II. skipWhile(조건)

subscribe하는 동안 모든 element들을 필터링하는 filter와 달리, skipWhile은 어떠한 element를 skip하지 않을 때까지 skip하고 종료하는 연산자입니다. 이게 무슨 말이냐! 하면.. 어떤 skipWhile 조건에 맞는 element들을 skip하다가, 어느순간 그 조건에 맞지 않는 element는 방출시키게 되겠죠? 그럼 이때부터는 skipWhile 조건이라는 제약이 풀려서 이후부터는 다시 조건에 맞는 element가 들어오더라도 신경쓰지 않고 방출한다~ 이 말입니다!

 

아래 예시를 보면, 처음에 int % 2 == 1 이라는 조건에 맞는 1과 3은 skip되었는데, 조건에 맞지 않는 4가 방출된 이후부터는 조건과 상관 없이 방출이 되고 있는 것을 확인할 수 있습니다.

let disposeBag = DisposeBag()

Observable.of(1,3,4,6,5,7,8)
    .skipWhile({ (int) -> Bool in
        int % 2 == 1
    })
    .subscribe(
        onNext: {
            print($0)
        }
    )
    .disposed(by: disposeBag)

 

[ 출력 ]

4

6

5

7

8

 

 

III. skipUntil(otherObservable)

앞서 살펴보았던 Skip Operator들은 고정된 조건에서 이루어졌습니다. 그럼 만약 다른 Observable에 기반한 element들을 dynamic하게 필터링하고 싶다면 어떻게 해야할까요? skipUntil을 사용하면 다른 Observable이 동작할 때까지 현재 Observable에서 방출하는 이벤트들을 skip합니다.

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")

 

[ 출력 ]

C

 

 

C. Taking Operators

I. take(n)

Taking은 Skipping의 반대 개념으로 생각하면 되는데, 우리가 어떤 element들을 취하고 싶을 때 take를 사용합니다. 예시를 보면, take로 설정한 처음 2개의 element를 취한 것을 볼 수 있습니다.

let disposeBag = DisposeBag()

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

 

[ 출력 ]

1

2

 

 

II. takeWhile(조건)

takeWhile은 skipWhile처럼 작동하는데, 위 마블다이어그램과 같이 takeWhile 구문 내에서 설정한 조건에서 true에 해당하는 값을 방출하게 됩니다.

 

 

III. enumerated( )

방출된 element의 index를 참고하고 싶은 경우가 있는데, enumerated 연산자를 사용하면 기존 swift의 enumerated 메서드와 유사하게 Observable에서 나오는 각 element의 index와 값을 포함하는 튜플을 생성하게 됩니다.

let disposeBag = DisposeBag()

Observable.of(1,2,3)
    .enumerated()
    .takeWhile({ index, value in
        value > 1 && index > 1
    })
    .map {
        $0.element
    }
    .subscribe(
        onNext: {
            print($0)
        }
    )
    .disposed(by: disposeBag)

 

[ 출력 ]

3

 

 

IV. takeUntil(otherObservable)

skipUntil은 trigger 역할을 하는 Observable이 subscribe 될 때까지 skip되었다면, takeUntiltrigger 역할을 하는 Observable이 subscribe되기 전까지의 이벤트값만 받게 됩니다.

let disposeBag = DisposeBag()

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

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

trigger.onNext("X")

subject.onNext("3")

 

[ 출력 ]

1

2

 

 

D. Distinct Operators

I. distinctUntilChanged( )

중복되어 이어지는 값을 막아줍니다. 그렇기 때문에 위 마블다이어그램을 보면 연속되어 중복되는 2는 방출되지 않고, 이후에 나오는 1은 중복이긴 하지만 연속되어 반복된 것이 아니기 때문에 방출됩니다.

let disposeBag = DisposeBag()

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

 

[ 출력 ]

1

2

1

 

 

II. distinctUntilChanged(_:)

위에서 살펴본 distinctUntilChanged는 기본적으로 구현된 로직을 바탕으로 중복을 확인합니다. 하지만 만약 우리가 커스텀한 비교로직을 사용하고 싶다면 distinctUntilChanged(_:)를 사용하면 됩니다.

struct Model {
    var value: Int
}

// ----------------------

let disposeBag = DisposeBag()

let model1 = Model(value: 1)
let model2 = Model(value: 1)
let model3 = Model(value: 2)

Observable<Model>.of(model1, model2, model3)
    .distinctUntilChanged({ o1, o2 in
        o1.value == o2.value
    })
    .subscribe(
        onNext: {
            print($0.value)
        }
    )
    .addDisposableTo(disposeBag)

 

[ 출력 ]

1

2

 

 

 

 

반응형

'iOS > RxSwift' 카테고리의 다른 글

Ch7. Transforming Operators  (0) 2023.02.24
RxSwift. Traits가 뭘까? (Single, Maybe, Completable)  (0) 2023.02.24
Ch3. Subjects  (0) 2023.02.24
Ch2. Observable  (0) 2023.02.24
Ch9. Combining Operators  (1) 2021.01.13

댓글