RxSwiftで、2回に1回イベントを流すObservableを作ってみた


2番煎じかもしれませんが、ざっと探したら見つからなかったので。
他にあれば教えて下さい(笑い

実装方針

  • scanを使って、Bool値をtrue -> false -> true -> falseのように都度切り替えて流す
  • そのBool値をつかって、filterする
let intSubject = PublishSubject<Int>()
let subject =
    intSubject
        .scan((true, 0)) { (output, update) -> (Bool, Int) in
            //Bool値を反転して返してあげると、ストリームの度に値が変わることになる
            //初期値はtrueを反転するのでfalseから始まる。結果として奇数回を飛ばすことになる
            return (!output.0, update)
        }
        .compactMap { (flag, value) -> Int? in
            flag ? value : nil
        }
subject.bind { print($0) }

intSubject.onNext(1)
intSubject.onNext(2)
intSubject.onNext(3)
intSubject.onNext(4)
intSubject.onNext(5)
intSubject.onNext(6)
//結果
2
4
6

compactMap使ってますので、流す型がInt? だとおかしくなりますね。素直に filterとmapで書いたほうが良さそうです。あとはscanの初期値として渡している 0 も型を特定する以上の役割がないので要らないかなぁ

汎用化してみました

extension をつかって、Observableの関数化してみます。

extension Observable {
    //isEvenは偶数回だけイベントを流すか、奇数回だけにするかを決定するフラグ
    func onceInTwice(isEven: Bool) -> Observable<Element> {
        let initial: (Bool,[Element]) = (isEven, [])
        return
            scan(initial) { (output, update) -> (Bool, [Element]) in
                return (!output.0, [update])
            }
            .filter { (flag, _) in return flag }
            .map { (_, value)  in return value.first! }
    }
}

let input = PublishSubject<String?>()
let onceInTwice = input.onceInTwice(isEven: false)

onceInTwice.bind { print($0) }

input.onNext("1回目")
input.onNext("2回目")
input.onNext(nil)
input.onNext("4回目")
input.onNext("5回目")
input.onNext("6回目")

//結果
Optional("1回目")
nil
Optional("5回目")

期待どおりに動いてくれてそうです〜。