Posted at

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

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