可观察的当前值和先前值
Posted
技术标签:
【中文标题】可观察的当前值和先前值【英文标题】:Observable Current and Previous Value 【发布时间】:2016-03-17 02:00:39 【问题描述】:我有一个变量,它是一个枚举值数组。这些值会随着时间而变化。
enum Option
case One
case Two
case Three
let options = Variable<[Option]>([ .One, .Two, .Three ])
然后我观察这个变量的变化。问题是,我需要知道最新值和以前值之间的差异。我目前正在这样做:
let previousOptions: [Option] = [ .One, .Two, .Three ]
...
options
.asObservable()
.subscribeNext [unowned self] opts in
// Do some work diff'ing previousOptions and opt
// ....
self.previousOptions = opts
RxSwift 中是否有内置的东西可以更好地处理这个问题?有没有办法始终从信号中获取先前和当前值?
【问题讨论】:
【参考方案1】:这是一个方便的通用扩展,它应该涵盖这些“我想要前一个值和当前值” 用例:
extension ObservableType
func withPrevious(startWith first: E) -> Observable<(E, E)>
return scan((first, first)) ($0.1, $1) .skip(1)
【讨论】:
太棒了!我只是像这样添加了参数名称:Observable<(previous: E, current: E)>
我只是从你的答案中使用了扫描,我以前不知道..谢谢!
此方法永远不会发出包含作为参数提供的first
元素的元组,请考虑删除.skip(1)
startWith
有什么用? observable 的当前/最后一个值?
@swearwolf 是的,如果您想要第一个值,请删除跳过调用。我还可以看到,这对我来说是误导性的命名。【参考方案2】:
你来了
options.asObservable()
.scan( [ [],[] ] ) seed, newValue in
return [ seed[1], newValue ]
// optional, working with tuple of array is better than array of array
.map array in (array[0], array[1])
//optional, in case you dont want empty array
.skipWhile $0.count == 0 && $1.count == 0
它将返回Observable<([Options], [Options])>
:)
【讨论】:
进一步,如何获取初始值?【参考方案3】:另一种扩展方式
extension ObservableType
func withPrevious() -> Observable<(E?, E)>
return scan([], accumulator: (previous, current) in
Array(previous + [current]).suffix(2)
)
.map( (arr) -> (previous: E?, current: E) in
(arr.count > 1 ? arr.first : nil, arr.last!)
)
用法:
someValue
.withPrevious()
.subscribe(onNext: (previous, current) in
if let previous = previous // previous is optional
print("previous: \(previous)")
print("current: \(current)")
)
.disposed(by: disposeBag)
【讨论】:
【参考方案4】:正如 Pham Hoan 所说,scan(_)
是完成这项工作的正确工具。 Marin Todorov 写了一封 good post 来说明这一点。
这是我根据 Marin 的帖子得出的结论:
options
.asObservable()
.scan([])
(previous, current) in
return Array(previous + [current]).suffix(2)
.subscribeNext
(lastTwoOptions) in
let previousOptions = lastTwoOptions.first
let currentOptions = lastTwoOptions.last
// Do your thing. Remember to check for nil the first time around!
.addDisposableTo(self.disposeBag)
希望有帮助
【讨论】:
【参考方案5】:.pairwise()
运算符完全符合您的要求,并且是最简单的方法。此运算符将成对的连续发射组合在一起,并将它们作为两个值的数组发射。
见:http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-pairwise
或https://rxjs-dev.firebaseapp.com/api/operators/pairwise
更新:正如@courteouselk 在他的评论中指出的那样,我没有注意到这是一个 RxSwift 问题,我的回答引用了一个 RxJS 解决方案(哎呀!)。
原来RxSwift 没有有一个内置的pairwise
运算符,但是RxSwiftExt 提供了一个类似于内置RxJS 运算符的pairwise 扩展运算符。
【讨论】:
@courteouselk,感谢您指出这一点,我已经参考 RxSwift 特定的成对运算符实现更新了我的答案【参考方案6】:一行中的最佳解决方案:
Observable.zip(options, options.skip(1))
【讨论】:
这适用于大多数情况,但需要注意的是,'options' 可能应该是一个共享序列,否则您将获得 2 个订阅,这可能是您不想要的,并可能导致意外结果. 例如让选项 = randomNumberProducingObservable(); Observable.zip(options, options.skip(1));不会产生你所期望的。确保声明 let options = randomNumberProducingObservable().share();像这样 @retendo 问题是关于如何从Variable
中检索值,在 RxSwift4 中由BehaviorRelay
替换,默认情况下是可共享的。
是的,如果它与变量/行为继电器有关,你是对的
这不好,因为第一次选项会触发下一次什么都不会发生,只有第二次才会发生【参考方案7】:
我会建议这样的事情(对于未来的访问者):
options.asObservable()
.map (old: [], new: $0) // change type from array to tuple
.scan((old: [], new: [])) previous, current in
// seed with an empty tuple & return both information
return (old: previous.new, new: current.new)
.subscribe(onNext: option in
let oldArray = option.old // old
let newArray = option.new // new
.addDisposableTo(disposeBag)
【讨论】:
以上是关于可观察的当前值和先前值的主要内容,如果未能解决你的问题,请参考以下文章
Scala Spark Dataframe 创建一个新列,其中包含另一列的最大先前值和当前值
R - 如何制作 n 个先前值的平均值/平均值,不包括当前观察值(滚动平均值)