Rxで何でもかんでもIObservable<T>
で書こうとすると
「trigger
ストリームの発行タイミングで、source
ストリームの最終値を後続に流したい」
ということがよくある。
例えば「ファイルに保存ボタンクリック」イベントストリームの発行タイミングで、「ファイル名」ストリームの現在値を後続に流したい。など。
適当にlatestValue
なるフィールドを用意して
source.Subscribe(x => latestValue = x);
trigger.Subscribe(_ => FooBar(latestValue));
こうしてやれば実現できるが、なるべくIObservableでやりたい気分のときどうするか。
Sample()
似たような動きをするのがSample()
メソッドであるが、
こいつはsource
が値を発行しない間にtrigger
を数発発行しても、最初の一発しかサンプリングしてくれない。
Sample()メソッド:
source ---1-------2------3------4--
trigger -------o----o-o--o----o-----
source.Sample(trigger)
-------1----2---------3-----
やりたい方:
source ---1-------2------3------4--
trigger -------o----o-o--o----o-----
???
-------1----2-2--2----3-----
CombineLatest()
triggerの分だけ発行してくれるが、sourceの変更でも発行してしまう。
source ---1-------2--------3------4--
trigger -------o-----o-o--o-----o-----
source.CombineLatest(trigger,(s,t)=>s)
-------1---2-2-2--2-3---3--4--
これではテキストボックスに文字打っただけで保存してしまう。
Sample().CombineLatest()
目的のストリームが得られる。
source ---1-------2------3------4--
trigger -------o----o-o--o----o-----
source.Sample(trigger)
-------1----2---------3-----
.CombineLatest(trigger,(s,t)=>s)
-------1----2-2--2----3-----
CombineLatest().DistictUntilChanged()
triggerをとにかく前回と違う値を流すストリームに変換して(例えばScan()でインクリメントさせる)
sourceとCombineLatest()して、trigger変換ストリームの値をキーにしてDistictUntilChanged()する。
source ---1-------2--------3------4--------
trigger -------o------o--o-----o------o-----
trigger' -------A------B--C-----D------E-----
.CombineLatest(trigger,(s,t)=>new {src = s, trg = t})
-------1A--2A-2B-2C-3C-3D--4D-4E----
.DistictUntilChanged(x => x.trg)
-------1A-----2B-2C----3D-----4E----
.Select(x => x.src)
-------1------2--2-----3------4-----
おわり
定番パターン集みたいなのが少ないので、いまいち自分がまわりくどいor不確実な実装をしているような気がしてならない。