Rxswift: distinctUntilChanged should have an option without a comparator for arrays of equatables

Created on 13 Jul 2017  路  7Comments  路  Source: ReactiveX/RxSwift

Short description of the issue:
The implementation of distinctUntilChanged in extension ObservableType where E: Equatable doesn't cover the case of arrays of equatable objects.

example:

Expected outcome:

Since there is an obvious default meaning of `untilChanged with arrays of equatables, I expect that I don't need to pass a closure to the operator.

What actually happens:

distinctUntilChanged() won't compile without a closure { $0 == $1 }

Self contained code example that reproduces the issue:

struct ThisIsEquatable: Equatable {
  public static func == (rhs: ThisIsEquatable, lhs: ThisIsEquatable) -> Bool {
    return true
  }
}

let equatables = (0..<10).map { _ in ThisIsEquatable() }
Observable.of(equatables).distinctUntilChanged()

RxSwift/RxCocoa/RxBlocking/RxTest version/commit
3.5.0

Platform/Environment

  • [*] iOS
  • [*] macOS
  • [*] tvOS
  • [*] watchOS
  • [*] playgrounds

How easy is to reproduce? (chances of successful reproduce after running the self contained code)

  • [*] easy, 100% repro

Xcode version:

8.3.2

:warning: Fields below are optional for general issues or in case those questions aren't related to your issue, but filling them out will increase the chances of getting your issue resolved. :warning:

Installation method:

  • [ ] CocoaPods
  • [*] Carthage
  • [ ] Git submodules

I have multiple versions of Xcode installed:
(so we can know if this is a potential cause of your issue)

  • [ ] yes (which ones)
  • [*] no

Level of RxSwift knowledge:
(this is so we can understand your level of knowledge
and formulate the response in an appropriate manner)

  • [ ] just starting
  • [ ] I have a small code base
  • [*] I have a significant code base

Most helpful comment

I would agree with @kzaher

But I guess you can add this convenience extension to your library

extension ObservableType where E: Sequence, E.Iterator.Element: Equatable {
    func distinctUntilChanged() -> Observable<E> {
        return distinctUntilChanged { (lhs, rhs) -> Bool in
            return Array(lhs) == Array(rhs)
        }
    }
}

All 7 comments

Hi @tsabend ,

yes, this is a awkward limitation of Swift compiler. We are expecting this to be solved on language level, otherwise we need to define extensions for all known types + Equatable.

Fair enough. I wonder if it's worth adding a shim for Arrays since it's a pretty common case, but not a big deal.

I would agree with @kzaher

But I guess you can add this convenience extension to your library

extension ObservableType where E: Sequence, E.Iterator.Element: Equatable {
    func distinctUntilChanged() -> Observable<E> {
        return distinctUntilChanged { (lhs, rhs) -> Bool in
            return Array(lhs) == Array(rhs)
        }
    }
}

But I feel like this could be inefficient if you operating on big sequences, but anyway overall should be O(n) time

@sergdort I think this is a good enough rule of thumb solution.
@tsabend I would rather not add this to this library at the moment.

Makes sense. Thanks for taking a look.

This may be solvable in a coming Swift version:

https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md

The reason I arrived here, is that I have a lot of subscriptions to arrays in global state (ReSwift), and as a result, I have a lot of distinctUntilChanged { $0 == $1 }. Aside from the runtime cost, there is a high compile time cost -- sometimes more than a second per each.

Was this page helpful?
0 / 5 - 0 ratings