Observable Current and Previous Value

Viewed 24639

I have a Variable which is an array of enum values. These values change over time.

enum Option {
    case One
    case Two
    case Three
}

let options = Variable<[Option]>([ .One, .Two, .Three ])

I then observe this variable for changes. The problem is, I need to know the diff between the newest value and the previous value. I'm currently doing this:

let previousOptions: [Option] = [ .One, .Two, .Three ]

...

options
    .asObservable()
    .subscribeNext { [unowned self] opts in
        // Do some work diff'ing previousOptions and opt
        // ....
        self.previousOptions = opts
    }

Is there something built in to RxSwift that would manage this better? Is there a way to always get the previous and current values from a signal?

7 Answers

Another way as an extension

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!)
      })
  }
}

Usage:

someValue
  .withPrevious()
  .subscribe(onNext: { (previous, current) in
    if let previous = previous { // previous is optional
      print("previous: \(previous)")
    }
    print("current: \(current)")
  })
  .disposed(by: disposeBag)

The .pairwise() operator does exactly what you want, and is the simplest way to do do it. This operator groups pairs of consecutive emissions together and emits them as an array of two values.

see: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-pairwise

or https://rxjs-dev.firebaseapp.com/api/operators/pairwise


UPDATE: as @courteouselk pointed out in his comment, I failed to notice that this was an RxSwift question, and my answer referenced an RxJS solution (oops!).

Turns out RxSwift does not have a built-in pairwise operator, but RxSwiftExt provides a pairwise extension operator similar to the built-in RxJS operator.

Best solution in one line:

Observable.zip(options, options.skip(1))
Related