【问题标题】: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 中是否有内置的东西可以更好地处理这个问题?有没有办法始终从信号中获取先前和当前值?

【问题讨论】:

    标签: swift rx-swift


    【解决方案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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-10-13
          • 2012-07-19
          • 2021-09-08
          • 1970-01-01
          相关资源
          最近更新 更多