티스토리 뷰

iOS

[Combine] Chapter4: Filtering Operators

Peppo 2022. 11. 3. 00:07
728x90

이전 챕터와 비슷하게 filtering 연산자에 대해 공부해 보려 합니다.

 

Filtering basics

 

filter()

Swift에 있는 filter와 같습니다. (조건문과 성립하는것만 걸러냄)
filter

var subscriptions = Set<AnyCancellable>()

example(of: "filter") {
    // 1. 1~10까지 이벤트 방출
    let numbers = (1...10).publisher
    
    // 2. filter 연산자로, 3의 배수만 걸러냄
    numbers
        .filter { $0.isMultiple(of: 3) }  // isMultiple of의 지정된 숫자의 배수이면 true 반환
        .sink(receiveValue: { num in
            print("\(num) 은 3의 배수")
        })
        .store(in: &subscriptions)
}

/*
 ——— Example of: filter ———
 3 은 3의 배수
 6 은 3의 배수
 9 은 3의 배수
*/

 

 

removeDuplicates()

중복된 값을 제거해줍니다.
removeDuplicates()

var subscriptions = Set<AnyCancellable>()

example(of: "removeDuplicate") {
    let words = "hey hey there! want to listen to mister mister ?"
        .components(separatedBy: " ") // " " 띄워쓰기 부분별로 나눠 배열에 담음
        // ["hey", "hey", "there!", "want", "to", "listen", "to", "mister", "mister", "?"]
        .publisher
    
    words
        .removeDuplicates() // 중복된 요소가 있을 경우 하나만 방출합니다.
        .sink(receiveValue: { print($0) })
        .store(in: &subscriptions)
}

/*
 ——— Example of: removeDuplicate ———
 hey
 there!
 want
 to
 listen
 to
 mister
 ?
*/

 

Compacting and ignoring

compactMap()

Swift의 compactMap과 같습니다. (nil일 경우 무시)

compactMap
var subscriptions = Set<AnyCancellable>()

example(of: "compactMap") {
    
    let strings = ["a", "1.24", "3", "def", "45", "0.23"].publisher

    strings
        .compactMap { Float($0) } // nil 무시
        .sink(receiveValue: { print($0) })
        .store(in: &subscriptions)
}

/*
 ——— Example of: compactMap ———
 1.24
 3.0
 45.0
 0.23
*/

 

ignoreOutput()

모든 값들(values)을 생략하고 completion 이벤트만 방출합니다.

ignoreOutput()

var subscriptions = Set<AnyCancellable>()

example(of: "ignoreOutput") {
    let numbers = (1...10_000).publisher
    
    numbers
        .ignoreOutput() // 모든 값들 생략
        .sink(receiveCompletion: { print("Completed with: \($0)") },
              receiveValue: { print($0) })
        .store(in: &subscriptions)
}

/*
 ——— Example of: ignoreOutput ———
 Completed with: finished
*/

 

 

Finding values

first(where:)

매칭되는 조건이 나오면, 첫번째요소를 emit 후 complete 됩니다.
first(where:)

var subscriptions = Set<AnyCancellable>()

example(of: "first(where:)") {
    
    let strings = ["bc", "ad", "abc"].publisher
    
    strings
         // "a"가 포함되어 있는 첫번째 요소 반환
        .first(where: { $0.contains(where: { char in char == "a" })} ) 
        .sink(receiveCompletion: { print("Completed with: \($0)")},
              receiveValue: { print($0) })
        .store(in: &subscriptions)
}

/*
 ——— Example of: first(where:) ———
 ad
 Completed with: finished
*/

 

last(where:)

매칭되는 조건이 나오면, 마지막요소를 emit 후 complete 됩니다.
var subscriptions = Set<AnyCancellable>()

example(of: "last(where:)") {
    
    let numbers = (1...9).publisher
    
    numbers
        .last(where: { $0 % 2 == 0 })
        .sink(receiveCompletion: { print("Completed with: \($0)") },
              receiveValue: { print($0) })
        .store(in: &subscriptions)
}
/*
 ——— Example of: last(where:) ———
 8
 Completed with: finished
*/​
var subscriptions = Set<AnyCancellable>()

example(of: "last(where:) 2") {
    let numbers = PassthroughSubject<Int, Never>()
    
    numbers
        .last(where: { $0 % 2 == 0 })
        .sink(receiveCompletion: { print("Completed with: \($0)") },
              receiveValue: { print($0) })
        .store(in: &subscriptions)
    
    numbers.send(1)
    numbers.send(2)
    numbers.send(3)
    numbers.send(4)
    numbers.send(5)
    numbers.send(completion: .finished)
}
/*
 ——— Example of: last(where:) 2 ———
 4
 Completed with: finished
*/

 

Dropping values

dropFirst()

dropFirst(숫자) 만큼 앞에서 떨굽니다.
dropFirst()
var subscriptions = Set<AnyCancellable>()

example(of: "dropFirst") {
    
    let numbers = (1...10).publisher
    
    numbers
        .dropFirst(8)  // 앞에서 8개를 떨굼
        .sink(receiveValue: { print($0) })
        .store(in: &subscriptions)
}
/*
 ——— Example of: dropFirst ———
 9
 10
*/

 

drop(while:)

조건에 만족하는 요소가 나오기전까지 다 생략합니다.
drop(while:)
var subscriptions = Set<AnyCancellable>()

example(of: "drop(while:)") {
    
    let numbers = (1...10).publisher
    
    numbers
        .drop(while: {
            print("x")
            return $0 % 5 != 0 }) // 조건에 만족하는 숫자가 나오기전 요소들은 다 생략
        .sink(receiveValue: { print($0) })
        .store(in: &subscriptions)
}
/*
 ——— Example of: drop(while:) ———
 x
 x
 x
 x
 x
 5
 6
 7
 8
 9
 10
*/​

 

drop(untilOutputFrom: isReady)

만약 특정 횟수 까지만 생략하고 이후 이벤트 부터는 emit 하고 싶다면

drop(untilOutputFrom: isReady)
var subscriptions = Set<AnyCancellable>()

example(of: "drop(untilOutputFrom:)") {
    
    let isReady = PassthroughSubject<Void, Never>()
    let taps = PassthroughSubject<Int, Never>()
    
    taps
        .drop(untilOutputFrom: isReady)
        .sink(receiveValue: { print($0) })
        .store(in: &subscriptions)
    
    (1...5).forEach { n in
        taps.send(n) // 1, 2, 3 까지 생략
        
        if n == 3 {
            isReady.send() // isReady 활성화 >> 이후 이벤트 emit
        }
    }
    
}

/*
 ——— Example of: drop(untilOutputFrom:) ———
 4
 5
*/​

 

 

Limiting values

 

prefix(_:)

drop과 반대로 파라미터의 숫자만큼만 emit

prefix(_:)

var subscriptions = Set<AnyCancellable>()

example(of: "prefix") {
    
    let numbers = (1...10).publisher
    
    numbers
        .prefix(2)
        .sink(receiveCompletion: { print("Completed with: \($0)") },
              receiveValue: { print($0) })
        .store(in: &subscriptions)
}
/*
 ——— Example of: prefix ———
 1
 2
 Completed with: finished
*/

 

prefix(while:)

조건에 만족하는 앞의 이벤트(들)만 emit

prefix(while:)
var subscriptions = Set<AnyCancellable>()

example(of: "prefix(while:)") {
    
    let numbers = (1...10).publisher
    
    numbers
        .prefix(while: { $0 < 3 })
        .sink(receiveCompletion: { print("Completed with: \($0)") },
              receiveValue: { print($0) })
        .store(in: &subscriptions)
    
}

/*
 ——— Example of: prefix(while:) ———
 1
 2
 Completed with: finished
*/

 

prefix(untilOutputFrom: isReady)

조건에 만족할때 까지 이벤트 emit

prefix(untilOutputFrom: isReady)
var subscriptions = Set<AnyCancellable>()

example(of: "prefix(untilOutputFrom: isReady)") {
    
    let isReady = PassthroughSubject<Void, Never>()
    let taps = PassthroughSubject<Int, Never>()
    
    taps
        .prefix(untilOutputFrom: isReady)
        .sink(receiveCompletion: { print("Completed with: \($0)") },
              receiveValue: { print($0) })
        .store(in: &subscriptions)
    
    (1...5).forEach { n in
        taps.send(n)
        
        if n == 2 {
            isReady.send()
        }
    }
}

/*
 ——— Example of: prefix(untilOutputFrom: isReady) ———
 1
 2
 Completed with: finished
*/​

 

 

Challenge

1. 1 ~ 100까지 중 50 부터 upstream에서 방출
2. 50 ~ 70 까지만 받기
3. 그중 짝수만 가져오기

var subscriptions = Set<AnyCancellable>()

example(of: "Challenge") {
    
    let numbers = (1...100).publisher
    
    numbers
        .dropFirst(50)
        .prefix(while: { $0 <= 70 })
        .filter { $0 % 2 == 0 }
        .sink(receiveValue: { print($0) })
        .store(in: &subscriptions)
}

/*
 ——— Example of: Challenge ———
 52
 54
 56
 58
 60
 62
 64
 66
 68
 70
*/​
728x90