关于ios:在RxSwift中滑动窗口

Sliding windows in RxSwift

出于RxJava的背景,我无法提出在RxSwift中实现滑动窗口的标准方法。 例如。 我有以下事件序列:

1
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ...

假设事件发射在一秒钟内发生两次。 我想要做的是将这个序列转换为一个缓冲区序列,每个缓冲区包含最后三秒钟的数据。 另外,每个缓冲区将每秒发出一次。 因此结果将如下所示:

1
[1,2,3,4,5,6], [3,4,5,6,7,8], [5,6,7,8,9,10], ...

我在RxJava中要做的是使用buffer方法的重载之一,如下所示:

1
stream.buffer(3000, 1000, TimeUnit.MILLISECONDS)

这正是我需要完成的结果:缓冲区序列,每个缓冲区每秒发出一次,并且包含最后三秒钟的数据。

我到处检查了RxSwift文档,但没有发现buffer运算符的任何重载,因此我无法这样做。 我是否缺少一些非显而易见的(对于RxJava用户,ofc)运算符?


我最初使用自定义运算符编写了解决方案。 从那以后,我就知道了如何使用标准运算符来完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extension ObservableType {

    func buffer(timeSpan: RxTimeInterval, timeShift: RxTimeInterval, scheduler: SchedulerType) -> Observable<[E]> {
        let trigger = Observable<Int>.timer(timeSpan, period: timeShift, scheduler: scheduler)
            .takeUntil(self.takeLast(1))

        let buffer = self
            .scan([Date: E]()) { previous, current in
                var next = previous
                let now = scheduler.now
                next[now] = current
                return next.filter { $0.key > now.addingTimeInterval(-timeSpan) }
        }

        return trigger.withLatestFrom(buffer)
            .map { $0.sorted(by: { $0.key <= $1.key }).map { $0.value } }
    }
}

我将以下原始解决方案留给后代使用:

解决方案是编写自己的运算符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
extension ObservableType {

    func buffer(timeSpan: RxTimeInterval, timeShift: RxTimeInterval, scheduler: SchedulerType) -> Observable<[E]> {
        return Observable.create { observer in
            var buf: [Date: E] = [:]
            let lock = NSRecursiveLock()
            let elementDispoable = self.subscribe { event in
                lock.lock(); defer { lock.unlock() }
                switch event {
                case let .next(element):
                    buf[Date()] = element
                case .completed:
                    observer.onCompleted()
                case let .error(error):
                    observer.onError(error)
                }
            }
            let spanDisposable = scheduler.schedulePeriodic((), startAfter: timeSpan, period: timeShift, action: { state in
                lock.lock(); defer { lock.unlock() }
                let now = Date()
                buf = buf.filter { $0.key > now.addingTimeInterval(-timeSpan) }
                observer.onNext(buf.sorted(by: { $0.key <= $1.key }).map { $0.value })
            })
            return Disposables.create([spanDisposable, elementDispoable])
        }
    }
}