可观察的当前值和先前值

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&lt;(previous: E, current: E)&gt; 我只是从你的答案中使用了扫描,我以前不知道..谢谢! 此方法永远不会发出包含作为参数提供的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&lt;([Options], [Options])&gt; :)

【讨论】:

进一步,如何获取初始值?【参考方案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 个先前值的平均值/平均值,不包括当前观察值(滚动平均值)

来自先前 ROW 值的 SUM

选择可观察的值和文本

SQL 查询 - 尝试返回包含先前记录和当前记录的结果的记录

如何在不使用 LOOP 的情况下计算取决于先前值的当前值?