web-dev-qa-db-fra.com

RxJS distinctUntilChanged - comparaison d'objets

J'ai un flux d'objets et je dois comparer si l'objet actuel n'est pas le même que le précédent et dans ce cas émettre une nouvelle valeur. J'ai trouvé que l'opérateur distinctUntilChanged devrait faire exactement ce que je veux, mais pour une raison quelconque, il n'émet jamais de valeur sauf la première. Si je supprime les valeurs distinctUntilChanged, elles sont émises normalement.

Mon code:

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
            }
        });
    }
}
23
Daniel Suchý

J'ai eu le même problème et l'ai résolu en utilisant JSON.stringify pour comparer les objets:

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

Code sale mais fonctionnel.

29
Mehdi Benmoha

Je trouve enfin où est le problème. Le problème était dans la version de RxJS, dans V4 et plus tôt, l'ordre des paramètres est différent de V5.

RxJS 4:

distinctUntilChanged = fonction (keyFn, comparateur)

RxJS 5:

distinctUntilChanged = fonction (comparateur, keyFn)

Dans tous les documents d'aujourd'hui, vous pouvez trouver l'ordre des paramètres V4, méfiez-vous!

21
Daniel Suchý

Quand vous avez de toute façon lodash dans votre application, vous pouvez simplement utiliser la fonction isEqual() de lodash, qui fait une comparaison approfondie et correspond parfaitement à la signature de distinctUntilChanged():

.distinctUntilChanged(isEqual),

Ou si vous avez _ disponible (ce qui n'est plus recommandé de nos jours):

.distinctUntilChanged(_.isEqual),
10
hoeni

La solution de Mehdi est rapide mais ne fonctionnera pas si la commande n'est pas maintenue. Utilisation de l'une des bibliothèques deep-equal ou fast-deep-equal :

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

De RxJS v6 +, il y a distinctUntilKeyChanged

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);
2
Whisher

Si vous modifiez la valeur de manière mutuelle, aucune des autres réponses ne fonctionne. Si vous devez travailler avec des structures de données mutables, vous pouvez utiliser distinctUntilChangedImmutable, qui copie en profondeur la précédente et si aucune fonction de comparaison n'est transmise, affirmera que les valeurs précédentes et actuelles profondes sont égales (et non === assertion).

0
Tom