Skip to content

DistinctUntilChanged overload for tuples with Equatable elements #1946

@klop

Description

@klop

Short description of the issue:

There currently exists an overload of distinctUntilChanged on ObservableType which takes no arguments and is constrained to where E: Equatable. It simply implements the comparer by delegating to == on the Equatable element. This is useful since it lets us do:

Observable<String>().distinctUntilChanged()

rather than:

Observable<String>().distinctUntilChanged { $0 == $1 }

Unfortunately, we're not afforded the same syntactic brevity when dealing with tuples whose elements all conform to Equatable.

Expected outcome:

Ideally, we'd be able to write something like:

Observable
    .combineLatest(
        Observable<String>(),
        Observable<Int>()
    )
    .distinctUntilChanged()

What actually happens:

Since tuples cannot conform to protocols, we're faced with a compiler error:

error: referencing instance method 'distinctUntilChanged()' on 'ObservableType' requires that '(String, Int)' conform to 'Equatable'

To get around this, we can apply distinctUntilChanged() onto both of the inner observables instead:

Observable
    .combineLatest(
        Observable<String>().distinctUntilChanged(),
        Observable<Int>().distinctUntilChanged()
    )

Or alternatively, we can ad-hoc implement our own comparer:

Observable
    .combineLatest(
        Observable<String>(),
        Observable<Int>()
    )
    .distinctUntilChanged { $0 == $1 }

The last example works not because the tuple conforms to Equatable, but because the standard library provides us with implementations of == and != for tuples of up to arity 6 (see Tuple Comparison). It's also the only solution for observables that begin as tuples, rather than the result of combining multiple observables.

Proposed solution:

It's possible to implement an overload (or more, depending on arity) of distinctUntilChanged for tuples that similarly takes no arguments, and implements comparer using the == overloads for tuples.

For example, for 2-element tuples:

extension ObservableType {
    public func distinctUntilChanged<A: Equatable, B: Equatable>() -> Observable<(A, B)> where E == (A, B) {
        return self.distinctUntilChanged { $0 == $1 }
    }
}

Resulting in the code seen in "Expected outcome" being valid.

I'd be more than happy to make a pull request if the proposal is considered worthwhile. Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions