RxJS distinctUntilChanged - сопоставление объектов

У меня есть поток объектов, и мне нужно сравнить, если текущий объект не такой же, как предыдущий, и в этом случае испускает новое значение. Я обнаружил, что оператор differUntilChanged должен делать именно то, что я хочу, но по какой-то причине он никогда не испускает значение, кроме первого. Если я удаляю значение differUntilChanged, выдается нормально.

Мой код:

export class SettingsPage {
    static get parameters() {
        return [[NavController], [UserProvider]];
    }

    constructor(nav, user) {
        this.nav = nav;
        this._user = user;

        this.typeChangeStream = new Subject();
        this.notifications = {};
    }

    ngOnInit() {

        this.typeChangeStream
            .map(x => {console.log('value on way to distinct', x); return x;})
            .distinctUntilChanged(x => JSON.stringify(x))
            .subscribe(settings => {
                console.log('typeChangeStream', settings);
                this._user.setNotificationSettings(settings);
            });
    }

    toggleType() {
        this.typeChangeStream.next({
            "sound": true,
            "vibrate": false,
            "badge": false,
            "types": {
                "newDeals": true,
                "nearDeals": true,
                "tematicDeals": false,
                "infoWarnings": false,
                "expireDeals": true
            }
        });
    }

    emitDifferent() {
        this.typeChangeStream.next({
            "sound": false,
            "vibrate": false,
            "badge": false,
            "types": {
                "newDeals": false,
                "nearDeals": false,
                "tematicDeals": false,
                "infoWarnings": false,
                "expireDeals": false
            }
        });
    }
}

Ответ 1

У меня была та же проблема и исправлена ​​с помощью JSON.stringify для сравнения объектов:

.distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))

Грязный, но рабочий код.

Ответ 2

Наконец-то выясню, где проблема. Проблема была в версии RxJS, в V4 и более ранних - порядок, отличный от V5.

RxJS 4:

distinctUntilChanged = function (keyFn, comparer)

RxJS 5:

distinctUntilChanged = function (comparer, keyFn)

В каждом документе сегодня вы можете найти порядок параметров V4, остерегайтесь этого!

Ответ 3

В любом случае, если в вашем приложении есть lodash, вы можете просто использовать функцию lodash isEqual(), которая выполняет глубокое сравнение и идеально соответствует сигнатуре isEqual() distinctUntilChanged():

.distinctUntilChanged(isEqual),

Или, если у вас есть _ наличии (что больше не рекомендуется в наши дни):

.distinctUntilChanged(_.isEqual),

Ответ 4

Решение Mehdi хоть и быстрое, но не сработает, если порядок не будет соблюден. Использование одной из библиотек с глубоким или быстрым глубоким равенством:

.distinctUntilChanged((a, b) => deepEqual(a, b))

Ответ 5

Если вы изменяете значение изменчиво, ни один из других ответов не сработает. Если вам нужно работать с изменчивыми структурами данных, вы можете использовать distinctUntilChangedImmutable, которая копирует глубокое предыдущее значение, и если функция сравнения не передается, будет утверждать, что предыдущее и текущее значения глубоко равны каждому другое (не === утверждение).

Ответ 6

От RxJS v6+ есть отличныйUntilKeyChanged

https://www.learnrxjs.io/operators/filtering/distinctuntilkeychanged.html

const source$ = from([
  { name: 'Brian' },
  { name: 'Joe' },
  { name: 'Joe' },
  { name: 'Sue' }
]);

source$
  // custom compare based on name property
  .pipe(distinctUntilKeyChanged('name'))
  // output: { name: 'Brian }, { name: 'Joe' }, { name: 'Sue' }
  .subscribe(console.log);