RxSwift,计算发出多少相等的连续Equatable项

时间:2018-05-03 21:14:44

标签: swift rx-swift

我有一个Observable,它发出一系列Equatable元素。 该流可以包含相等元素的连续序列(例如,在序列[1,1,1,18,2,2,0,-1]中,元素1重复3次,2重复2次)。 我需要压缩序列,将每个不同的元素替换为包含元素的元组和原始流中存在的重复次数:

1, 1, 1, 18, 21, 21, 0, -1, -8, -8, 14, 14, 14...

(1, 3), (18, 1), (21, 2), (0, 1), (-1, 1), (-8, 2), (14, 3)...

我设法用scan运算符计算重复次数,但是它会发出不应该成为最终序列一部分的所有部分计算:

let numbers = Observable<Int>.from([
    0, 0,
    3, 3, 3, 3, 3,
    2,
    0, 0, 0,
    6, 6,
    ])

let reps = numbers
    .scan((0, 0), accumulator: {
        (prev: (Int, Int), new: (Int)) in
        if prev.0 == new {
            return (new, prev.1 + 1)
        } else {
            return (new, 1)
        }
    })

reps.subscribe(onNext: {
        print("\($0)")
    })

// expected:
// (0, 2), (3, 5), (2, 1), (0, 3), (6, 2)
//
// result:
// (0, 1), (0, 2),
// (3, 1), (3, 2), (3, 3), (3, 4), (3, 5),
// (2, 1),
// (0, 1), (0, 2), (0, 3),
// (6, 1), (6, 2)

2 个答案:

答案 0 :(得分:2)

您可以编写自己的运算符。这是一个示例实现。我创建了一个新的observable,它订阅self个事件。任何时候self都有一个新元素,.next中的switch案例会被点击,这会影响游戏的预订。只要遇到不同的元素,错误或完成,就会发出分组。

extension ObservableType where Self.E: Equatable {
    func runLengthEncode() -> Observable<(element: E, count: Int)> {
        var lastGrouping: (element: E, count: Int)? = nil

        return Observable.create { observer in
            return self.subscribe { event in
                switch event {
                case .next(let currentElement):
                    if let currentGrouping = lastGrouping {
                        if currentGrouping.element == currentElement {
                            lastGrouping = (element: currentElement, count: currentGrouping.count + 1)
                        }
                        else { // This run ended, a new element was encountered.
                            lastGrouping = (element: currentElement, count: 1) // start a new grouping
                            observer.on(.next(currentGrouping)) // emit the completed grouping
                        }
                    } else {
                        lastGrouping = (element: currentElement, count: 1)
                    }

                case .error(let error):
                    if let lastGrouping = lastGrouping { observer.on(.next(lastGrouping)) } // Emit the last unemitted grouping.
                    observer.on(.error(error))

                case .completed:
                    if let lastGrouping = lastGrouping { observer.on(.next(lastGrouping)) } // Emit the last unemitted grouping.
                    observer.on(.completed)
                }
            }
        }
    }
}

您还可以实现一个免费的运行长度解码运算符:

extension ObservableType {
    func runLengthDecode<Element>() -> Observable<Element>
        where Self.E == (element: Element, count: Int) {
        return Observable.create { observer in
            return self.subscribe { event in
                switch event {
                case .next((element: let element, count: let count)):
                    for _ in 1...count {
                        observer.on(.next(element))
                    }


                case .error(let error): observer.on(.error(error))
                case .completed: observer.on(.completed)
                }
            }
        }
    }
}

测试用例:

let numbers = Observable<Int>.from([
    0, 0,
    3, 3, 3, 3, 3,
    2,
    0, 0, 0,
    6, 6,
])

let runLengthEncoded = numbers.runLengthEncode()
runLengthEncoded.subscribe { print($0) }

let runLengthDecoded = runLengthEncoded.runLengthDecode()
runLengthDecoded.subscribe { print($0) }

答案 1 :(得分:0)

我想我想出了一个可能的解决方案:

  1. 创建一个Optional<T>的新观察点(其中TEquatable中的numbers元素)从numbers中删除第一个项目并附加(concatnil元素。

  2. zip已生成reps Observable的Observable。

  3. filter生成的Observable。

  4. // step 1
    let shiftedNumbers = Observable<Int?> = numbers
        .skip(1)
        .map({ $0 })
        .concat(Observable<Int?>.just(nil))
    
    // step 2 and 3
    let zippedNumbers = Observable<(Int, Int)?>
        .zip(counts, shortNumbers, resultSelector: {
            if $1 == nil || $1 != $0.0 {
                return $0
            } else {
                return nil
            }
        })
        .filter({
            $0 != nil
        })
    
    zippedNumbers.subscribe(onNext: {
        print($0!)
    })
    
    // result:
    // (0, 2), (3, 5), (2, 1), (0, 3), (6, 2)
    

    跳过numbers序列的第一个元素,可以“提前看到”下一个元素将为我们提供发出正确元组的机会。