swift 可观察的当前值和先前值

lg40wkob  于 2023-01-25  发布在  Swift
关注(0)|答案(8)|浏览(107)

我有一个变量,它是一个枚举值数组。这些值随时间变化。

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中是否有内置的东西可以更好地管理这个问题?是否有一种方法可以总是从信号中获得以前和当前的值?

i34xakig

i34xakig1#

下面是一个方便的通用扩展,它应该可以覆盖这些 “我想要以前和当前的值” 用例:

extension ObservableType {
    
    func withPrevious(startWith first: Element) -> Observable<(Element, Element)> {
        return scan((first, first)) { ($0.1, $1) }.skip(1)
    }
}
vc6uscn9

vc6uscn92#

给你

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])>:)

flseospp

flseospp3#

另一种方式作为延伸

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)
vjrehmav

vjrehmav4#

正如Pham Hoan所说,scan(_)是完成这项工作的合适工具,Marin Todorov写了一篇很好的文章。
以下是我根据马林的帖子得出的结论:

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)

希望能有所帮助

v9tzhpje

v9tzhpje5#

.pairwise()运算符完全符合您的要求,并且是最简单的方法。该运算符将连续发射对组合在一起,并将它们作为两个值的数组发射。
参见:www.example.comhttp://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运算符类似的成对扩展运算符。

fkvaft9z

fkvaft9z6#

一条生产线的最佳解决方案:

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

vsmadaxz7#

我会建议这样的东西(对未来的游客):

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)
r8xiu3jd

r8xiu3jd8#

TL;医生:

如果您想要一个具有previouscurrent值的元组,而不跳过第一个流事件,也不指定初始值-这里有一个解决方案(在本例中,previous值是Optional)。
我意识到已经有很多答案了,但当尝试它们时,我似乎总是想要一些稍微不同的东西。
我喜欢元组方法,但我不想错过第一次发射(skip(1))-我看到一些解具有startWith值,但我想要一个不需要提供初始种子值的解,并且tuple.previous值为Optional(表示“尚无先前值”)。
这是我的想法,有点冗长(有意地,为了让它更容易阅读,随意缩短语法)。此外,我不得不约束ElementEquatable。也许有更好的方法(请评论),但现在这是我的工作:

extension ObservableType where Element: Equatable {
    /// Returns a tuple with `.previous` and `.current` value of the stream.
    /// `.previous` is optional, because it might be the first stream value and no previous values exist yet
    func withPrevious() -> Observable<(previous: Element?, current: Element)>  {
        scan((nil, nil)) { (accumulatedValue: (previous:Element?, current: Element?), newValue: Element) -> (previous: Element?, current: Element?) in
            if accumulatedValue == (nil, nil) {
                return (previous: nil, current: newValue)
            } else {
                return (previous: accumulatedValue.current, current: newValue)
            }
        }
        .compactMap { (previous: Element?, current: Element?) -> (previous: Element?, current: Element)? in
            guard let current else { return nil }
            return (previous: previous, current: current)
        }
    }
}

示例用法:

Observable<Int>.from([1, 2, 3, 4])
    .withPrevious()
    .debug()
    .subscribe()
    .dispose()

控制台输出:

-> subscribed
-> Event next((previous: nil, current: 1))
-> Event next((previous: Optional(1), current: 2))
-> Event next((previous: Optional(2), current: 3))
-> Event next((previous: Optional(3), current: 4))
-> Event completed
-> isDisposed

作为一个额外的-这里是上面的方法,但是如果它对你的用例有意义的话,它有一个startWith值-所以.previous值不再是Optional。这与其他人在其他答案中已经建议的是等价的(但是语法更长)。

/// Returns a tuple with `.previous` and `.current` value of the stream.
/// Expects a starting value which will be the `.previous` value of the first emission
func withPrevious(startWith: Element) -> Observable<(previous: Element, current: Element)>  {
    scan((startWith, startWith)) { (accumulatedValue: (previous: Element, current: Element), newValue: Element) -> (previous: Element, current: Element) in
        if accumulatedValue == (startWith, startWith) {
            return (previous: startWith, current: newValue)
        } else {
            return (previous: accumulatedValue.current, current: newValue)
        }
    }
}

相关问题