훌이
후리스콜링개발
훌이

블로그 메뉴

  • 왈 (iOS APP)
  • Github
전체 방문자
오늘
어제
  • 전체 (171)
    • ⭐️ 개발 (140)
      • JAVA (4)
      • Web (5)
      • iOS & Swift (94)
      • iOS Concurrency (4)
      • Rx (18)
      • Git (6)
      • WWDC (1)
      • Code Refactor (3)
      • Server (1)
    • ⭐️ Computer Science (22)
      • 운영체제 (10)
      • 네트워크 (5)
      • PS (7)
    • 경제시사상식 (8)
    • 기타 등등 (0)

인기 글

최근 글

05-13 03:56

티스토리

hELLO · Designed By 정상우.
훌이

후리스콜링개발

⭐️ 개발/Rx

[Rx] Filtering Operator

2023. 3. 18. 14:16
728x90
반응형

Filtering Operator

ignoreElement

elementAt

skip

take

debounce

throttle

filter

single

distinctUntilChanged

 

ignoreElement

// MARK: - IgnoreElements next 이벤트를 필터링하는 연산자

let friends = ["소깡", "디온", "방구"]

Observable.from(friends)
    .ignoreElements()
    .subscribe { print($0) }
    .disposed(by: bag)
//completed

 

elementAt

// MARK: - elementAt 특정 인덱스에 위치한 요소를 제한적으로 방출하는 연산자

Observable.from(friends)
    .element(at: 2)
    .subscribe { print($0) }
    .disposed(by: bag)
//next(방구)

 

filter

// MARK: - filter 특정 조건에 맞는 항목을 필터링

let num = [1,2,3,4,5,6,7,8]
Observable.from(num)
    .filter { $0.isMultiple(of: 2) }
    .subscribe { print($0)}
    .disposed(by: bag)
//next(2)
//next(4)
//next(6)
//next(8)

 

skip

// MARK: - skip 지정한 수만큼 next 이벤트 무시하는 연산자

// index 아님 count임
Observable.from(num)
    .skip(3) // 이 숫자만큼 무시 -> 3개 무시
    .subscribe { print($0)}
    .disposed(by: bag)
//next(4)
//next(5)
//next(6)
//next(7)
//next(8)





// MARK: - skip(until:) 트리거 옵저버블이 이벤트를 방출하기 전까지 next 이벤트 무시

// Observable을 파라미터로 받고, 파라미터로 받은 이 옵저버블 즉, 트리거 옵저버블이 이벤트 방출해야 그때서야 전달
let subject = PublishSubject<Int>()
let trigger = PublishSubject<Int>()

subject.skip(until: trigger)
    .subscribe { print($0)}
    .disposed(by: bag)

subject.onNext(1) // trigger가 이벤트를 방출하지 않아서 전달x
trigger.onNext(100)
subject.onNext(20) // 얘만 방출함
//next(20)





// MARK: - skip(while:) 조건에 따라 이벤트 방출을 결정하는 연산자

// filter와 다르게 한 번이라도 false를 방출하면 그 후부터 쭉 다 방출
let nums = [1,2,3,4,5,6,7,8]
// 홀수는 true라 skip이니까 1을 skip, 2부터는 false라 그 후부터 다 방출
Observable.from(nums)
    .skip { !$0.isMultiple(of: 2)}
    .subscribe { print($0)}
    .disposed(by: bag)
//next(2)
//next(3)
//next(4)
//next(5)
//next(6)
//next(7)
//next(8)






// MARK: - skip(duration:schedular:) 지정한 시간동안 이벤트 방출을 무시하는 연산자

let number = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)

// 시간 처리 시 오차가 발생함!!! -> 1초마다 방출하는 게 아니다.
// 그래서 원래는 3초를 무시해야 하니까, 0, 1, 2 무시하고 3부터 방출해야 할 것 같지만 2부터 방출하는 것.
// 항상 오차가 있다는 거를 알고 사용하자!
//number
//    .take(10)
//    .skip(.seconds(3), scheduler: MainScheduler.instance)
//    .subscribe { print($0)}
//    .disposed(by: bag)
//next(2)
//next(3)
//next(4)
//next(5)
//next(6)
//next(7)
//next(8)
//next(9)

 

take

// MARK: - take 처음 n개의 이벤트만 방출하는 연산자

let numbers = [1,2,3,4,5]
Observable.from(numbers)
    .take(5)
    .subscribe { print($0)}
    .disposed(by: bag)






// MARK: - take(while:) 조건을 충족하는 동안 이벤트를 방출하는 연산자

// while : true면 방출, false면 X
// behavior : 마지막에 확인한 값을 방출할 건지 말지
// - .inclusive면 조건에 충족하지 않아도 방출 (보통 잘 사용하지 않음)
// - 기본값은 .exclusive

let my = [11,12,13,14,15]
Observable.from(my)
    .take(while: { !$0.isMultiple(of: 2) }) // 홀수만 방출 -> 12나오는 순간 false라 전달X
    .subscribe { print($0) }
    .disposed(by: bag)
//next(11)



Observable.from(my)
    .take(while: { !$0.isMultiple(of: 2) }, behavior: .inclusive)
    .subscribe { print($0) }
    .disposed(by: bag)
//next(11)
//next(12) -> 마지막에 확인한 값이 조건을 충족하지 않지만 .inclusive라 방출






// MARK: - take(until:) 트리거 옵저버블이 next 이벤트를 방출하기 전까지 이벤트를 방출

let mySubject = PublishSubject<Int>()
let myTrigger = PublishSubject<Int>()

/**
 1. 옵저버블을 파라미터로 받고, 이 옵저버블이 next event를 전달하기 전까지 원본 옵저버블에서 next event를 전달
 */
mySubject.take(until: myTrigger)
    .subscribe { print($0) }
    .disposed(by: bag)

mySubject.onNext(100)
//next(100)
mySubject.onNext(200)
//next(100)
//next(200)
myTrigger.onNext(0)
//next(100)
//next(200)
//completed
mySubject.onNext(300)

//300은 더이상 방출되지 않음.
//-> myTrigger 옵저버블이 이벤트를 전달했기 때문에 원본인 mySubject가 전달하는 건 더이상X




/**
 2. 클로저에서 false를 전달하면 이벤트 방출을 중단하고 옵저버블 종료
 */
mySubject.take(until: { $0 > 5})
    .subscribe {print($0)}
    .disposed(by: bag)

mySubject.onNext(1)
mySubject.onNext(2)
//next(1)
//next(2)
mySubject.onNext(10) // -> 5보다 큰 순간 false 중단.






// MARK: - takeLast 원본 옵저버블의 마지막 n개 이벤트만 방출하는 takeLast 연산자

let subject2 = PublishSubject<Int>()

subject2
    .takeLast(2) // -> 마지막 2개의 이벤트를 버퍼에 저장해둠
    .subscribe { print($0) }
    .disposed(by: bag)

(1...10).forEach { subject2.onNext($0) } // -> 9, 10을 저장해두고 있는 상태

subject2.onNext(11) // -> 10과 11로 버퍼에 업데이트
subject2.onCompleted() // -> 방출
//next(10)
//next(11)

enum MyError: Error {
    case error
}
subject2.onError(MyError.error) // 만약 completed 방출 전에 error event 방출시키면 error이벤트만 전달
//error(error)







// MARK: - take(for:scheduler:) 지정한 시간동안 이벤트를 방출하는 take(for:scheduler:)

let number2 = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)

number2
    .take(for: .seconds(5), scheduler: MainScheduler.instance)
    .subscribe { print($0) }
    .disposed(by: bag)
//next(0)
//next(1) -> 시간 오차가 있기 때문에 0, 1까지만 방출하고 끝남 (오차가 있다는 것을 알고 사용하자.)
//completed

 

 

single

// MARK: - single 하나의 요소가 방출되는 것을 보장하는 연산자

let number3 = [1,2,3,4,5,6,7,8,9,10]

Observable.just(1)
    .single()
    .subscribe { print($0)}
    .disposed(by: bag)
//next(1)



// single 연산자는 단 하나의 요소만 방출되어야 종료됨
Observable.from(number3)
    .single()
    .subscribe { print($0)}
    .disposed(by: bag)
//error(Sequence contains more than one element.)



// 3인 경우만 방출
Observable.from(number3)
    .single { $0 == 3 }
    .subscribe { print($0)}
    .disposed(by: bag)
//next(3)
//completed

let sub = PublishSubject<Int>()

sub.single()
    .subscribe { print($0)}
    .disposed(by: bag)

sub.onNext(100) // 하나의 요소가 전달됐다고 해서 바로 completed event를 전달하지 않음
// -> 또 다른 요소를 방출할 수 있기 때문에

 

distinctUntilChanged

// MARK: - distinctUntilChanged 동일한 요소가 연속적으로 방출되는 것을 막아주는 연산자

struct People {
    let name: String
    let age: Int
}

let numberArr = [1111,1111,300,20,20,30,1111,40,40,80,80,80]
let tuples = [(1,"하나"),(1,"일"),(1,"one")]
let people = [
    People(name: "루이", age: 13),
    People(name: "후이", age: 13),
    People(name: "부리", age: 23)
]



/**
 기본버전 - distinctUntilChanged()
 방출하는 이벤트가 이전 이벤트랑 비교해서 같으면 방출하지 않음
 */
Observable.from(numberArr)
    .distinctUntilChanged()
    .subscribe { print($0) }
    .disposed(by: bag)

//동일한 이벤트가 연속적으로 방출되면 하나만 방출함
//동일한 이벤트가 연속적으로 방출된 게 아니면 그냥 방출함. -> 1111
//next(1111)
//next(300)
//next(20)
//next(30)
//next(1111)
//next(40)
//next(80)




/**
 - .distinctUntilChanged(comparer:)
 직접 비교하고 싶으면 comparer라는 클로저를 통해서 가능
 */
Observable.from(numberArr)
    .distinctUntilChanged { !$0.isMultiple(of: 2) && !$1.isMultiple(of: 2) } // 둘 다 홀수이면 방출
    .subscribe { print($0) }
    .disposed(by: bag)

// 즉, 연속된 수가 홀수면 같다고 판단하는 것임
// 그래서 처음에 있는 1111, 1111만 연속으로 홀수라서 한 번만 방출
//next(1111)
//next(300)
//next(20)
//next(20)
//next(30)
//next(1111)
//next(40)
//next(40)
//next(80)
//next(80)
//next(80)




/**
- .distinctUntilChanged(keySelector:)
 튜플처럼 여러값을 갖고 있는 compound value인 경우에 사용하고,
 비교할 값을 넣어줄 때는 Equatable을 따라야 한다.
 */
Observable.from(tuples)
    .distinctUntilChanged { $0.0 }
    .subscribe { print($0) }
    .disposed(by: bag)
//next((1, "하나")) -> 튜플의 첫 번째 값은 모두 같아서 1개만 전달




/**
 - .distinctUntilChanged(at: KeyPath<옵저버블타입, Equatable>)
 파라미터로 비교할 속성을 넣어줌
 */
Observable.from(people)
    .distinctUntilChanged(at: \.age)
    .subscribe { print($0) }
    .disposed(by: bag)
//next(People(name: "루이", age: 13)) -> 후이와 루이는 나이가 같아서 후이는 전달X
//next(People(name: "부리", age: 23))

 

Debounce

 

지정된 시간 동안 이벤트가 발생하지 않으면 가장 마지막 이벤트를 구독자에게 전달함

 

(검색기능 입력 시 사용, 키워드 입력 시의 네트워크 호출, 데이터베이스 검색)

- 지정된 시간 동안 검색이 더 이뤄지지 않으면 검색 작업 실행

- dueTime : 시간. next event를 방출할 지 결정하는 조건,
 옵저버가 next event를 방출한 다음 또 다른 next event를 지정된 시간 내에 방출하지 않는다면 해당 시점의 가장 마지막 이벤트를 구독자에게 전달
 반대로, 지정된 시간 내에 방출한다면 타이머를 초기화함. 그리고 지정된 시간 내에 대기함.
 - scheduler : 타이머를 실행할 스케줄러
let buttonTap = Observable<String>.create { observer in
    DispatchQueue.global().async {
        
        // 0.3초 주기로 10번 방출
        for i in 1...10 {
            observer.onNext("Tap \(i)")
            Thread.sleep(forTimeInterval: 0.3)
        }
        
        // 1초동안 스레드 중지
        Thread.sleep(forTimeInterval: 1)
        
        // 0.5초 주기로 10번 방출
        for i in 11...20 {
            observer.onNext("Tap \(i)")
            Thread.sleep(forTimeInterval: 0.5)
        }
        observer.onCompleted()
    }
    return Disposables.create { }
}

buttonTap
    .debounce(.milliseconds(1000), scheduler: MainScheduler.instance)
    .subscribe { print($0) }
    .disposed(by: bag)
    
// 0.3초 주기로 1~10을 방출하는데
// debounce도 1초가 타이머인데 그때마다 next event가 방출되니까 타이머가 갱신되는 것임
// 그리고 1초를 쉬니까 그때 이제 가장 마지막 방출된 10이 전달된 것
//next(Tap 10)
//next(Tap 20) -> 20도 마찬가지
//completed

 

Throttle

next event를 지정된 주기(dueTime)마다 구독자에게 전달 (반복되는 탭 이벤트)

- latest

1. true : true를 전달하면 주기를 정확히 지킴
2. false : false면 지정된 주기가 지나고 그 이후에 방출되는 next event가 전달된다.

즉, true는 지정된 주기를 엄격하게 지키지만, false는 주기를 초과할 수 있다.
// 1초마다 정수를 방출하는 옵저버블에서 2.5초 주기를 가진 throttle 연산자
// debug는 이벤트 발생시간을 체크하기 위함

Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .debug()
    .take(10)
    .throttle(.milliseconds(2500), latest: true, scheduler: MainScheduler.instance)
    .subscribe { print($0) }
    .disposed(by: bag)

// true를 전달하면 주기를 정확히 지킴 2.5초마다 가장 최근에 방출한 값을 구독자에게 전달

//2023-03-18 14:06:52.995: Filtering.playground:403 (__lldb_expr_174) -> Event next(0)
//next(0)
//2023-03-18 14:06:53.995: Filtering.playground:403 (__lldb_expr_174) -> Event next(1)
//2023-03-18 14:06:54.994: Filtering.playground:403 (__lldb_expr_174) -> Event next(2)
//next(2)
//2023-03-18 14:06:55.994: Filtering.playground:403 (__lldb_expr_174) -> Event next(3)
//2023-03-18 14:06:56.995: Filtering.playground:403 (__lldb_expr_174) -> Event next(4)
//2023-03-18 14:06:57.995: Filtering.playground:403 (__lldb_expr_174) -> Event next(5)
//next(5)
//2023-03-18 14:06:58.994: Filtering.playground:403 (__lldb_expr_174) -> Event next(6)
//2023-03-18 14:06:59.994: Filtering.playground:403 (__lldb_expr_174) -> Event next(7)
//next(7)
//2023-03-18 14:07:00.993: Filtering.playground:403 (__lldb_expr_174) -> Event next(8)
//2023-03-18 14:07:01.993: Filtering.playground:403 (__lldb_expr_174) -> Event next(9)
//2023-03-18 14:07:01.993: Filtering.playground:403 (__lldb_expr_174) -> isDisposed
//next(9)


Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .debug()
    .take(10)
    .throttle(.milliseconds(2500), latest: false, scheduler: MainScheduler.instance)
    .subscribe { print($0) }
    .disposed(by: bag)

// latest가 false면 지정된 주기가 지나고 그 이후에 방출되는 next event가 전달된다.
//2023-03-18 14:09:08.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(0)
//next(0)
//2023-03-18 14:09:09.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(1)
//2023-03-18 14:09:10.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(2)
//2023-03-18 14:09:11.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(3)
//next(3)
//2023-03-18 14:09:12.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(4)
//2023-03-18 14:09:13.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(5)
//2023-03-18 14:09:14.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(6)
//next(6)
//2023-03-18 14:09:15.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(7)
//2023-03-18 14:09:16.343: Filtering.playground:430 (__lldb_expr_178) -> Event next(8)
//2023-03-18 14:09:17.344: Filtering.playground:430 (__lldb_expr_178) -> Event next(9)
//next(9)
//completed
728x90
반응형
저작자표시 비영리 변경금지

'⭐️ 개발 > Rx' 카테고리의 다른 글

[Rx] Time Based Operator  (0) 2023.03.20
[Rx] Conditional Operator  (0) 2023.03.19
[Rx] Create Operators  (0) 2023.03.17
[Rx Operator 시리즈] 4. flatMap / flatMapFirst / flatMapLatest  (0) 2023.01.19
[Rx] NSObject+Rx 라이브러리  (1) 2023.01.18
    '⭐️ 개발/Rx' 카테고리의 다른 글
    • [Rx] Time Based Operator
    • [Rx] Conditional Operator
    • [Rx] Create Operators
    • [Rx Operator 시리즈] 4. flatMap / flatMapFirst / flatMapLatest
    훌이
    훌이

    티스토리툴바