fix
杨锴
2025-06-16 3fa53409f5132333ce6d83fff796e108ddd62090
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//
//  DistinctUntilChanged.swift
//  RxSwift
//
//  Created by Krunoslav Zaher on 3/15/15.
//  Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
 
extension ObservableType where Element: Equatable {
 
    /**
     Returns an observable sequence that contains only distinct contiguous elements according to equality operator.
 
     - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
 
     - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence.
     */
    public func distinctUntilChanged()
        -> Observable<Element> {
        self.distinctUntilChanged({ $0 }, comparer: { ($0 == $1) })
    }
}
 
extension ObservableType {
    /**
     Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`.
 
     - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
 
     - parameter keySelector: A function to compute the comparison key for each element.
     - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
     */
    public func distinctUntilChanged<Key: Equatable>(_ keySelector: @escaping (Element) throws -> Key)
        -> Observable<Element> {
        self.distinctUntilChanged(keySelector, comparer: { $0 == $1 })
    }
 
    /**
     Returns an observable sequence that contains only distinct contiguous elements according to the `comparer`.
 
     - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
 
     - parameter comparer: Equality comparer for computed key values.
     - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence.
     */
    public func distinctUntilChanged(_ comparer: @escaping (Element, Element) throws -> Bool)
        -> Observable<Element> {
        self.distinctUntilChanged({ $0 }, comparer: comparer)
    }
 
    /**
     Returns an observable sequence that contains only distinct contiguous elements according to the keySelector and the comparer.
 
     - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
 
     - parameter keySelector: A function to compute the comparison key for each element.
     - parameter comparer: Equality comparer for computed key values.
     - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence.
     */
    public func distinctUntilChanged<K>(_ keySelector: @escaping (Element) throws -> K, comparer: @escaping (K, K) throws -> Bool)
        -> Observable<Element> {
            return DistinctUntilChanged(source: self.asObservable(), selector: keySelector, comparer: comparer)
    }
 
    /**
    Returns an observable sequence that contains only contiguous elements with distinct values in the provided key path on each object.
 
    - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
 
    - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator on the provided key path
    */
    public func distinctUntilChanged<Property: Equatable>(at keyPath: KeyPath<Element, Property>) ->
        Observable<Element> {
        self.distinctUntilChanged { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
    }
}
 
final private class DistinctUntilChangedSink<Observer: ObserverType, Key>: Sink<Observer>, ObserverType {
    typealias Element = Observer.Element 
    
    private let parent: DistinctUntilChanged<Element, Key>
    private var currentKey: Key?
    
    init(parent: DistinctUntilChanged<Element, Key>, observer: Observer, cancel: Cancelable) {
        self.parent = parent
        super.init(observer: observer, cancel: cancel)
    }
    
    func on(_ event: Event<Element>) {
        switch event {
        case .next(let value):
            do {
                let key = try self.parent.selector(value)
                var areEqual = false
                if let currentKey = self.currentKey {
                    areEqual = try self.parent.comparer(currentKey, key)
                }
                
                if areEqual {
                    return
                }
                
                self.currentKey = key
                
                self.forwardOn(event)
            }
            catch let error {
                self.forwardOn(.error(error))
                self.dispose()
            }
        case .error, .completed:
            self.forwardOn(event)
            self.dispose()
        }
    }
}
 
final private class DistinctUntilChanged<Element, Key>: Producer<Element> {
    typealias KeySelector = (Element) throws -> Key
    typealias EqualityComparer = (Key, Key) throws -> Bool
    
    private let source: Observable<Element>
    fileprivate let selector: KeySelector
    fileprivate let comparer: EqualityComparer
    
    init(source: Observable<Element>, selector: @escaping KeySelector, comparer: @escaping EqualityComparer) {
        self.source = source
        self.selector = selector
        self.comparer = comparer
    }
    
    override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
        let sink = DistinctUntilChangedSink(parent: self, observer: observer, cancel: cancel)
        let subscription = self.source.subscribe(sink)
        return (sink: sink, subscription: subscription)
    }
}